1/* Copyright (C) 2013, 2015, Alexey Botchkov and SkySQL Ab
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
15
16
17#define PLUGIN_VERSION 0x104
18#define PLUGIN_STR_VERSION "1.4.4"
19
20#define _my_thread_var loc_thread_var
21
22#include <my_config.h>
23#include <assert.h>
24
25#ifndef _WIN32
26#include <syslog.h>
27#else
28#define syslog(PRIORITY, FORMAT, INFO, MESSAGE_LEN, MESSAGE) do {}while(0)
29static void closelog() {}
30#define openlog(IDENT, LOG_NOWAIT, LOG_USER) do {}while(0)
31
32/* priorities */
33#define LOG_EMERG 0 /* system is unusable */
34#define LOG_ALERT 1 /* action must be taken immediately */
35#define LOG_CRIT 2 /* critical conditions */
36#define LOG_ERR 3 /* error conditions */
37#define LOG_WARNING 4 /* warning conditions */
38#define LOG_NOTICE 5 /* normal but significant condition */
39#define LOG_INFO 6 /* informational */
40#define LOG_DEBUG 7 /* debug-level messages */
41
42#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri))
43
44/* facility codes */
45#define LOG_KERN (0<<3) /* kernel messages */
46#define LOG_USER (1<<3) /* random user-level messages */
47#define LOG_MAIL (2<<3) /* mail system */
48#define LOG_DAEMON (3<<3) /* system daemons */
49#define LOG_AUTH (4<<3) /* security/authorization messages */
50#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
51#define LOG_LPR (6<<3) /* line printer subsystem */
52#define LOG_NEWS (7<<3) /* network news subsystem */
53#define LOG_UUCP (8<<3) /* UUCP subsystem */
54#define LOG_CRON (9<<3) /* clock daemon */
55#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */
56#define LOG_FTP (11<<3) /* ftp daemon */
57#define LOG_LOCAL0 (16<<3) /* reserved for local use */
58#define LOG_LOCAL1 (17<<3) /* reserved for local use */
59#define LOG_LOCAL2 (18<<3) /* reserved for local use */
60#define LOG_LOCAL3 (19<<3) /* reserved for local use */
61#define LOG_LOCAL4 (20<<3) /* reserved for local use */
62#define LOG_LOCAL5 (21<<3) /* reserved for local use */
63#define LOG_LOCAL6 (22<<3) /* reserved for local use */
64#define LOG_LOCAL7 (23<<3) /* reserved for local use */
65
66#endif /*!_WIN32*/
67
68/*
69 Defines that can be used to reshape the pluging:
70 #define MARIADB_ONLY
71 #define USE_MARIA_PLUGIN_INTERFACE
72*/
73
74#if !defined(MYSQL_DYNAMIC_PLUGIN) && !defined(MARIADB_ONLY)
75#include <typelib.h>
76#define MARIADB_ONLY
77#endif /*MYSQL_PLUGIN_DYNAMIC*/
78
79#ifndef MARIADB_ONLY
80#define MYSQL_SERVICE_LOGGER_INCLUDED
81#endif /*MARIADB_ONLY*/
82
83#include <my_global.h>
84#include <my_base.h>
85#include <typelib.h>
86#include <mysql/plugin.h>
87#include <mysql/plugin_audit.h>
88#include <string.h>
89#ifndef RTLD_DEFAULT
90#define RTLD_DEFAULT NULL
91#endif
92
93#ifndef MARIADB_ONLY
94#undef MYSQL_SERVICE_LOGGER_INCLUDED
95#undef MYSQL_DYNAMIC_PLUGIN
96#define FLOGGER_NO_PSI
97
98/* How to access the pthread_mutex in mysql_mutex_t */
99#ifdef SAFE_MUTEX
100#define mysql_mutex_real_mutex(A) &(A)->m_mutex.mutex
101#else
102#define mysql_mutex_real_mutex(A) &(A)->m_mutex
103#endif
104
105#define flogger_mutex_init(A,B,C) do{}while(0)
106#define flogger_mutex_destroy(A) do{}while(0)
107#define flogger_mutex_lock(A) do{}while(0)
108#define flogger_mutex_unlock(A) do{}while(0)
109
110static char **int_mysql_data_home;
111static char *default_home= (char *)".";
112#define mysql_data_home (*int_mysql_data_home)
113
114#define FLOGGER_SKIP_INCLUDES
115#define my_open(A, B, C) loc_open(A, B)
116#define my_close(A, B) loc_close(A)
117#define my_rename(A, B, C) loc_rename(A, B)
118#define my_tell(A, B) loc_tell(A)
119#define my_write(A, B, C, D) loc_write(A, B, C)
120#define my_malloc(A, B) malloc(A)
121#define my_free(A) free(A)
122#ifdef my_errno
123 #undef my_errno
124#endif
125static int loc_file_errno;
126#define my_errno loc_file_errno
127#ifdef my_vsnprintf
128 #undef my_vsnprintf
129#endif
130#define my_vsnprintf vsnprintf
131#define logger_open loc_logger_open
132#define logger_close loc_logger_close
133#define logger_write loc_logger_write
134#define logger_rotate loc_logger_rotate
135#define logger_init_mutexts loc_logger_init_mutexts
136
137
138static size_t loc_write(File Filedes, const uchar *Buffer, size_t Count)
139{
140 size_t writtenbytes;
141#ifdef _WIN32
142 writtenbytes= (size_t)_write(Filedes, Buffer, (unsigned int)Count);
143#else
144 writtenbytes= write(Filedes, Buffer, Count);
145#endif
146 return writtenbytes;
147}
148
149
150static File loc_open(const char *FileName, int Flags)
151 /* Path-name of file */
152 /* Read | write .. */
153 /* Special flags */
154{
155 File fd;
156#ifdef _WIN32
157 HANDLE h;
158 /*
159 We could just use _open() here. but prefer to open in unix-similar way
160 just like my_open() does it on Windows.
161 This gives atomic multiprocess-safe appends, and possibility to rename
162 or even delete file while it is open, and CRT lacks this features.
163 */
164 assert(Flags == (O_APPEND | O_CREAT | O_WRONLY));
165 h= CreateFile(FileName, FILE_APPEND_DATA,
166 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL,
167 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
168 if (h == INVALID_HANDLE_VALUE)
169 {
170 fd= -1;
171 my_osmaperr(GetLastError());
172 }
173 else
174 {
175 fd= _open_osfhandle((intptr)h, O_WRONLY|O_BINARY);
176 }
177#else
178 fd= open(FileName, Flags, my_umask);
179#endif
180 my_errno= errno;
181 return fd;
182}
183
184
185static int loc_close(File fd)
186{
187 int err;
188#ifndef _WIN32
189 do
190 {
191 err= close(fd);
192 } while (err == -1 && errno == EINTR);
193#else
194 err= close(fd);
195#endif
196 my_errno=errno;
197 return err;
198}
199
200
201static int loc_rename(const char *from, const char *to)
202{
203 int error = 0;
204
205#if defined(__WIN__)
206 if (!MoveFileEx(from, to, MOVEFILE_COPY_ALLOWED |
207 MOVEFILE_REPLACE_EXISTING))
208 {
209 my_osmaperr(GetLastError());
210#elif defined(HAVE_RENAME)
211 if (rename(from,to))
212 {
213#else
214 if (link(from, to) || unlink(from))
215 {
216#endif
217 my_errno=errno;
218 error = -1;
219 }
220 return error;
221}
222
223
224static my_off_t loc_tell(File fd)
225{
226 os_off_t pos= IF_WIN(_telli64(fd),lseek(fd, 0, SEEK_CUR));
227 if (pos == (os_off_t) -1)
228 {
229 my_errno= errno;
230 }
231 return (my_off_t) pos;
232}
233
234#ifdef HAVE_PSI_INTERFACE
235#undef HAVE_PSI_INTERFACE
236#include <mysql/service_logger.h>
237#include "../../mysys/file_logger.c"
238#define HAVE_PSI_INTERFACE
239#else
240#include <mysql/service_logger.h>
241#include "../../mysys/file_logger.c"
242#endif
243#endif /*!MARIADB_ONLY*/
244
245#undef flogger_mutex_init
246#undef flogger_mutex_destroy
247#undef flogger_mutex_lock
248#undef flogger_mutex_unlock
249
250#define flogger_mutex_init(A,B,C) pthread_mutex_init(mysql_mutex_real_mutex(B), C)
251#define flogger_mutex_destroy(A) pthread_mutex_destroy(mysql_mutex_real_mutex(A))
252#define flogger_mutex_lock(A) pthread_mutex_lock(mysql_mutex_real_mutex(A))
253#define flogger_mutex_unlock(A) pthread_mutex_unlock(mysql_mutex_real_mutex(A))
254
255#ifndef DBUG_OFF
256#define PLUGIN_DEBUG_VERSION "-debug"
257#else
258#define PLUGIN_DEBUG_VERSION ""
259#endif /*DBUG_OFF*/
260/*
261 Disable __attribute__() on non-gcc compilers.
262*/
263#if !defined(__attribute__) && !defined(__GNUC__)
264#define __attribute__(A)
265#endif
266
267#ifdef _WIN32
268#define localtime_r(a, b) localtime_s(b, a)
269#endif /*WIN32*/
270
271
272extern char server_version[];
273static const char *serv_ver= NULL;
274static int started_mysql= 0;
275static int mysql_57_started= 0;
276static int debug_server_started= 0;
277static int use_event_data_for_disconnect= 0;
278static int started_mariadb= 0;
279static int maria_55_started= 0;
280static int maria_above_5= 0;
281static char *incl_users, *excl_users,
282 *file_path, *syslog_info;
283static char path_buffer[FN_REFLEN];
284static unsigned int mode, mode_readonly= 0;
285static ulong output_type;
286static ulong syslog_facility, syslog_priority;
287
288static ulonglong events; /* mask for events to log */
289static unsigned long long file_rotate_size;
290static unsigned int rotations;
291static my_bool rotate= TRUE;
292static char logging;
293static int internal_stop_logging= 0;
294static char incl_user_buffer[1024];
295static char excl_user_buffer[1024];
296static char *big_buffer= NULL;
297static size_t big_buffer_alloced= 0;
298static unsigned int query_log_limit= 0;
299
300static char servhost[256];
301static uint servhost_len;
302static char *syslog_ident;
303static char syslog_ident_buffer[128]= "mysql-server_auditing";
304
305struct connection_info
306{
307 int header;
308 unsigned long thread_id;
309 unsigned long long query_id;
310 char db[256];
311 int db_length;
312 char user[64];
313 int user_length;
314 char host[64];
315 int host_length;
316 char ip[64];
317 int ip_length;
318 const char *query;
319 int query_length;
320 char query_buffer[1024];
321 time_t query_time;
322 int log_always;
323};
324
325#define DEFAULT_FILENAME_LEN 16
326static char default_file_name[DEFAULT_FILENAME_LEN+1]= "server_audit.log";
327
328static void update_file_path(MYSQL_THD thd, struct st_mysql_sys_var *var,
329 void *var_ptr, const void *save);
330static void update_file_rotate_size(MYSQL_THD thd, struct st_mysql_sys_var *var,
331 void *var_ptr, const void *save);
332static void update_file_rotations(MYSQL_THD thd, struct st_mysql_sys_var *var,
333 void *var_ptr, const void *save);
334static void update_incl_users(MYSQL_THD thd, struct st_mysql_sys_var *var,
335 void *var_ptr, const void *save);
336static void update_excl_users(MYSQL_THD thd, struct st_mysql_sys_var *var,
337 void *var_ptr, const void *save);
338static void update_output_type(MYSQL_THD thd, struct st_mysql_sys_var *var,
339 void *var_ptr, const void *save);
340static void update_syslog_facility(MYSQL_THD thd, struct st_mysql_sys_var *var,
341 void *var_ptr, const void *save);
342static void update_syslog_priority(MYSQL_THD thd, struct st_mysql_sys_var *var,
343 void *var_ptr, const void *save);
344static void update_mode(MYSQL_THD thd, struct st_mysql_sys_var *var,
345 void *var_ptr, const void *save);
346static void update_logging(MYSQL_THD thd, struct st_mysql_sys_var *var,
347 void *var_ptr, const void *save);
348static void update_syslog_ident(MYSQL_THD thd, struct st_mysql_sys_var *var,
349 void *var_ptr, const void *save);
350static void rotate_log(MYSQL_THD thd, struct st_mysql_sys_var *var,
351 void *var_ptr, const void *save);
352
353static MYSQL_SYSVAR_STR(incl_users, incl_users, PLUGIN_VAR_RQCMDARG,
354 "Comma separated list of users to monitor.",
355 NULL, update_incl_users, NULL);
356static MYSQL_SYSVAR_STR(excl_users, excl_users, PLUGIN_VAR_RQCMDARG,
357 "Comma separated list of users to exclude from auditing.",
358 NULL, update_excl_users, NULL);
359/* bits in the event filter. */
360#define EVENT_CONNECT 1
361#define EVENT_QUERY_ALL 2
362#define EVENT_QUERY 122
363#define EVENT_TABLE 4
364#define EVENT_QUERY_DDL 8
365#define EVENT_QUERY_DML 16
366#define EVENT_QUERY_DCL 32
367#define EVENT_QUERY_DML_NO_SELECT 64
368
369static const char *event_names[]=
370{
371 "CONNECT", "QUERY", "TABLE", "QUERY_DDL", "QUERY_DML", "QUERY_DCL",
372 "QUERY_DML_NO_SELECT", NULL
373};
374static TYPELIB events_typelib=
375{
376 array_elements(event_names) - 1, "", event_names, NULL
377};
378static MYSQL_SYSVAR_SET(events, events, PLUGIN_VAR_RQCMDARG,
379 "Specifies the set of events to monitor. Can be CONNECT, QUERY, TABLE,"
380 " QUERY_DDL, QUERY_DML, QUERY_DML_NO_SELECT, QUERY_DCL.",
381 NULL, NULL, 0, &events_typelib);
382#define OUTPUT_SYSLOG 0
383#define OUTPUT_FILE 1
384#define OUTPUT_NO 0xFFFF
385static const char *output_type_names[]= { "syslog", "file", 0 };
386static TYPELIB output_typelib=
387{
388 array_elements(output_type_names) - 1, "output_typelib",
389 output_type_names, NULL
390};
391static MYSQL_SYSVAR_ENUM(output_type, output_type, PLUGIN_VAR_RQCMDARG,
392 "Desired output type. Possible values - 'syslog', 'file'"
393 " or 'null' as no output.", 0, update_output_type, OUTPUT_FILE,
394 &output_typelib);
395static MYSQL_SYSVAR_STR(file_path, file_path, PLUGIN_VAR_RQCMDARG,
396 "Path to the log file.", NULL, update_file_path, default_file_name);
397static MYSQL_SYSVAR_ULONGLONG(file_rotate_size, file_rotate_size,
398 PLUGIN_VAR_RQCMDARG, "Maximum size of the log to start the rotation.",
399 NULL, update_file_rotate_size,
400 1000000, 100, ((long long) 0x7FFFFFFFFFFFFFFFLL), 1);
401static MYSQL_SYSVAR_UINT(file_rotations, rotations,
402 PLUGIN_VAR_RQCMDARG, "Number of rotations before log is removed.",
403 NULL, update_file_rotations, 9, 0, 999, 1);
404static MYSQL_SYSVAR_BOOL(file_rotate_now, rotate, PLUGIN_VAR_OPCMDARG,
405 "Force log rotation now.", NULL, rotate_log, FALSE);
406static MYSQL_SYSVAR_BOOL(logging, logging,
407 PLUGIN_VAR_OPCMDARG, "Turn on/off the logging.", NULL,
408 update_logging, 0);
409static MYSQL_SYSVAR_UINT(mode, mode,
410 PLUGIN_VAR_OPCMDARG, "Auditing mode.", NULL, update_mode, 0, 0, 1, 1);
411static MYSQL_SYSVAR_STR(syslog_ident, syslog_ident, PLUGIN_VAR_RQCMDARG,
412 "The SYSLOG identifier - the beginning of each SYSLOG record.",
413 NULL, update_syslog_ident, syslog_ident_buffer);
414static MYSQL_SYSVAR_STR(syslog_info, syslog_info,
415 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
416 "The <info> string to be added to the SYSLOG record.", NULL, NULL, "");
417static MYSQL_SYSVAR_UINT(query_log_limit, query_log_limit,
418 PLUGIN_VAR_OPCMDARG, "Limit on the length of the query string in a record.",
419 NULL, NULL, 1024, 0, 0x7FFFFFFF, 1);
420
421char locinfo_ini_value[sizeof(struct connection_info)+4];
422
423static MYSQL_THDVAR_STR(loc_info,
424 PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_MEMALLOC,
425 "Internal info", NULL, NULL, locinfo_ini_value);
426
427static const char *syslog_facility_names[]=
428{
429 "LOG_USER", "LOG_MAIL", "LOG_DAEMON", "LOG_AUTH",
430 "LOG_SYSLOG", "LOG_LPR", "LOG_NEWS", "LOG_UUCP",
431 "LOG_CRON",
432#ifdef LOG_AUTHPRIV
433 "LOG_AUTHPRIV",
434#endif
435#ifdef LOG_FTP
436 "LOG_FTP",
437#endif
438 "LOG_LOCAL0", "LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3",
439 "LOG_LOCAL4", "LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7",
440 0
441};
442#ifndef _WIN32
443static unsigned int syslog_facility_codes[]=
444{
445 LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH,
446 LOG_SYSLOG, LOG_LPR, LOG_NEWS, LOG_UUCP,
447 LOG_CRON,
448#ifdef LOG_AUTHPRIV
449 LOG_AUTHPRIV,
450#endif
451#ifdef LOG_FTP
452 LOG_FTP,
453#endif
454 LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3,
455 LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7,
456};
457#endif
458static TYPELIB syslog_facility_typelib=
459{
460 array_elements(syslog_facility_names) - 1, "syslog_facility_typelib",
461 syslog_facility_names, NULL
462};
463static MYSQL_SYSVAR_ENUM(syslog_facility, syslog_facility, PLUGIN_VAR_RQCMDARG,
464 "The 'facility' parameter of the SYSLOG record."
465 " The default is LOG_USER.", 0, update_syslog_facility, 0/*LOG_USER*/,
466 &syslog_facility_typelib);
467
468static const char *syslog_priority_names[]=
469{
470 "LOG_EMERG", "LOG_ALERT", "LOG_CRIT", "LOG_ERR",
471 "LOG_WARNING", "LOG_NOTICE", "LOG_INFO", "LOG_DEBUG",
472 0
473};
474
475#ifndef _WIN32
476static unsigned int syslog_priority_codes[]=
477{
478 LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
479 LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG,
480};
481#endif
482
483static TYPELIB syslog_priority_typelib=
484{
485 array_elements(syslog_priority_names) - 1, "syslog_priority_typelib",
486 syslog_priority_names, NULL
487};
488static MYSQL_SYSVAR_ENUM(syslog_priority, syslog_priority, PLUGIN_VAR_RQCMDARG,
489 "The 'priority' parameter of the SYSLOG record."
490 " The default is LOG_INFO.", 0, update_syslog_priority, 6/*LOG_INFO*/,
491 &syslog_priority_typelib);
492
493
494static struct st_mysql_sys_var* vars[] = {
495 MYSQL_SYSVAR(incl_users),
496 MYSQL_SYSVAR(excl_users),
497 MYSQL_SYSVAR(events),
498 MYSQL_SYSVAR(output_type),
499 MYSQL_SYSVAR(file_path),
500 MYSQL_SYSVAR(file_rotate_size),
501 MYSQL_SYSVAR(file_rotations),
502 MYSQL_SYSVAR(file_rotate_now),
503 MYSQL_SYSVAR(logging),
504 MYSQL_SYSVAR(mode),
505 MYSQL_SYSVAR(syslog_info),
506 MYSQL_SYSVAR(syslog_ident),
507 MYSQL_SYSVAR(syslog_facility),
508 MYSQL_SYSVAR(syslog_priority),
509 MYSQL_SYSVAR(query_log_limit),
510 MYSQL_SYSVAR(loc_info),
511 NULL
512};
513
514
515/* Status variables for SHOW STATUS */
516static int is_active= 0;
517static long log_write_failures= 0;
518static char current_log_buf[FN_REFLEN]= "";
519static char last_error_buf[512]= "";
520
521extern void *mysql_v4_descriptor;
522
523static struct st_mysql_show_var audit_status[]=
524{
525 {"server_audit_active", (char *)&is_active, SHOW_BOOL},
526 {"server_audit_current_log", current_log_buf, SHOW_CHAR},
527 {"server_audit_writes_failed", (char *)&log_write_failures, SHOW_LONG},
528 {"server_audit_last_error", last_error_buf, SHOW_CHAR},
529 {0,0,0}
530};
531
532#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI)
533/* These belong to the service initialization */
534static PSI_mutex_key key_LOCK_operations;
535static PSI_mutex_key key_LOCK_bigbuffer;
536static PSI_mutex_info mutex_key_list[]=
537{
538 { &key_LOCK_operations, "SERVER_AUDIT_plugin::lock_operations",
539 PSI_FLAG_GLOBAL},
540 { &key_LOCK_bigbuffer, "SERVER_AUDIT_plugin::lock_bigbuffer",
541 PSI_FLAG_GLOBAL}
542};
543#endif
544static mysql_mutex_t lock_operations;
545static mysql_mutex_t lock_bigbuffer;
546
547/* The Percona server and partly MySQL don't support */
548/* launching client errors in the 'update_variable' methods. */
549/* So the client errors just disabled for them. */
550/* The possible solution is to implement the 'check_variable'*/
551/* methods there properly, but at the moment i'm not sure it */
552/* worths doing. */
553#define CLIENT_ERROR if (!started_mysql) my_printf_error
554
555static uchar *getkey_user(const char *entry, size_t *length,
556 my_bool nu __attribute__((unused)) )
557{
558 const char *e= entry;
559 while (*e && *e != ' ' && *e != ',')
560 ++e;
561 *length= e - entry;
562 return (uchar *) entry;
563}
564
565
566static void blank_user(char *user)
567{
568 for (; *user && *user != ','; user++)
569 *user= ' ';
570}
571
572
573static void remove_user(char *user)
574{
575 char *start_user= user;
576 while (*user != ',')
577 {
578 if (*user == 0)
579 {
580 *start_user= 0;
581 return;
582 }
583 user++;
584 }
585 user++;
586 while (*user == ' ')
587 user++;
588
589 do {
590 *(start_user++)= *user;
591 } while (*(user++));
592}
593
594
595static void remove_blanks(char *user)
596{
597 char *user_orig= user;
598 char *user_to= user;
599 char *start_tok;
600 int blank_name;
601
602 while (*user != 0)
603 {
604 start_tok= user;
605 blank_name= 1;
606 while (*user !=0 && *user != ',')
607 {
608 if (*user != ' ')
609 blank_name= 0;
610 user++;
611 }
612 if (!blank_name)
613 {
614 while (start_tok <= user)
615 *(user_to++)= *(start_tok++);
616 }
617 if (*user == ',')
618 user++;
619 }
620 if (user_to > user_orig && user_to[-1] == ',')
621 user_to--;
622 *user_to= 0;
623}
624
625
626struct user_name
627{
628 size_t name_len;
629 char *name;
630};
631
632
633struct user_coll
634{
635 int n_users;
636 struct user_name *users;
637 int n_alloced;
638};
639
640
641static void coll_init(struct user_coll *c)
642{
643 c->n_users= 0;
644 c->users= 0;
645 c->n_alloced= 0;
646}
647
648
649static void coll_free(struct user_coll *c)
650{
651 if (c->users)
652 {
653 free(c->users);
654 coll_init(c);
655 }
656}
657
658
659static int cmp_users(const void *ia, const void *ib)
660{
661 const struct user_name *a= (const struct user_name *) ia;
662 const struct user_name *b= (const struct user_name *) ib;
663 int dl= (int)(a->name_len - b->name_len);
664 if (dl != 0)
665 return dl;
666
667 return strncmp(a->name, b->name, a->name_len);
668}
669
670
671static char *coll_search(struct user_coll *c, const char *n, size_t len)
672{
673 struct user_name un;
674 struct user_name *found;
675 un.name_len= len;
676 un.name= (char *) n;
677 found= (struct user_name*) bsearch(&un, c->users, c->n_users,
678 sizeof(c->users[0]), cmp_users);
679 return found ? found->name : 0;
680}
681
682
683static int coll_insert(struct user_coll *c, char *n, size_t len)
684{
685 if (c->n_users >= c->n_alloced)
686 {
687 c->n_alloced+= 128;
688 if (c->users == NULL)
689 c->users= malloc(c->n_alloced * sizeof(c->users[0]));
690 else
691 c->users= realloc(c->users, c->n_alloced * sizeof(c->users[0]));
692
693 if (c->users == NULL)
694 return 1;
695 }
696 c->users[c->n_users].name= n;
697 c->users[c->n_users].name_len= len;
698 c->n_users++;
699 return 0;
700}
701
702
703static void coll_sort(struct user_coll *c)
704{
705 qsort(c->users, c->n_users, sizeof(c->users[0]), cmp_users);
706}
707
708
709static int user_coll_fill(struct user_coll *c, char *users,
710 struct user_coll *cmp_c, int take_over_cmp)
711{
712 char *orig_users= users;
713 char *cmp_user= 0;
714 size_t cmp_length;
715 int refill_cmp_coll= 0;
716
717 c->n_users= 0;
718
719 while (*users)
720 {
721 while (*users == ' ')
722 users++;
723 if (!*users)
724 return 0;
725
726 (void) getkey_user(users, &cmp_length, FALSE);
727 if (cmp_c)
728 {
729 cmp_user= coll_search(cmp_c, users, cmp_length);
730
731 if (cmp_user && take_over_cmp)
732 {
733 internal_stop_logging= 1;
734 CLIENT_ERROR(1, "User '%.*s' was removed from the"
735 " server_audit_excl_users.",
736 MYF(ME_JUST_WARNING), (int) cmp_length, users);
737 internal_stop_logging= 0;
738 blank_user(cmp_user);
739 refill_cmp_coll= 1;
740 }
741 else if (cmp_user)
742 {
743 internal_stop_logging= 1;
744 CLIENT_ERROR(1, "User '%.*s' is in the server_audit_incl_users, "
745 "so wasn't added.", MYF(ME_JUST_WARNING), (int) cmp_length, users);
746 internal_stop_logging= 0;
747 remove_user(users);
748 continue;
749 }
750 }
751 if (coll_insert(c, users, cmp_length))
752 return 1;
753 while (*users && *users != ',')
754 users++;
755 if (!*users)
756 break;
757 users++;
758 }
759
760 if (refill_cmp_coll)
761 {
762 remove_blanks(excl_users);
763 return user_coll_fill(cmp_c, excl_users, 0, 0);
764 }
765
766 if (users > orig_users && users[-1] == ',')
767 users[-1]= 0;
768
769 coll_sort(c);
770
771 return 0;
772}
773
774
775enum sa_keywords
776{
777 SQLCOM_NOTHING=0,
778 SQLCOM_DDL,
779 SQLCOM_DML,
780 SQLCOM_GRANT,
781 SQLCOM_CREATE_USER,
782 SQLCOM_CHANGE_MASTER,
783 SQLCOM_CREATE_SERVER,
784 SQLCOM_SET_OPTION,
785 SQLCOM_ALTER_SERVER,
786 SQLCOM_TRUNCATE,
787 SQLCOM_QUERY_ADMIN,
788 SQLCOM_DCL,
789};
790
791struct sa_keyword
792{
793 int length;
794 const char *wd;
795 struct sa_keyword *next;
796 enum sa_keywords type;
797};
798
799
800struct sa_keyword xml_word= {3, "XML", 0, SQLCOM_NOTHING};
801struct sa_keyword user_word= {4, "USER", 0, SQLCOM_NOTHING};
802struct sa_keyword data_word= {4, "DATA", 0, SQLCOM_NOTHING};
803struct sa_keyword server_word= {6, "SERVER", 0, SQLCOM_NOTHING};
804struct sa_keyword master_word= {6, "MASTER", 0, SQLCOM_NOTHING};
805struct sa_keyword password_word= {8, "PASSWORD", 0, SQLCOM_NOTHING};
806struct sa_keyword function_word= {8, "FUNCTION", 0, SQLCOM_NOTHING};
807struct sa_keyword statement_word= {9, "STATEMENT", 0, SQLCOM_NOTHING};
808struct sa_keyword procedure_word= {9, "PROCEDURE", 0, SQLCOM_NOTHING};
809
810
811struct sa_keyword keywords_to_skip[]=
812{
813 {3, "SET", &statement_word, SQLCOM_QUERY_ADMIN},
814 {0, NULL, 0, SQLCOM_DDL}
815};
816
817
818struct sa_keyword not_ddl_keywords[]=
819{
820 {4, "DROP", &function_word, SQLCOM_QUERY_ADMIN},
821 {4, "DROP", &procedure_word, SQLCOM_QUERY_ADMIN},
822 {4, "DROP", &user_word, SQLCOM_DCL},
823 {6, "CREATE", &user_word, SQLCOM_DCL},
824 {6, "CREATE", &function_word, SQLCOM_QUERY_ADMIN},
825 {6, "CREATE", &procedure_word, SQLCOM_QUERY_ADMIN},
826 {6, "RENAME", &user_word, SQLCOM_DCL},
827 {0, NULL, 0, SQLCOM_DDL}
828};
829
830
831struct sa_keyword ddl_keywords[]=
832{
833 {4, "DROP", 0, SQLCOM_DDL},
834 {5, "ALTER", 0, SQLCOM_DDL},
835 {6, "CREATE", 0, SQLCOM_DDL},
836 {6, "RENAME", 0, SQLCOM_DDL},
837 {8, "TRUNCATE", 0, SQLCOM_DDL},
838 {0, NULL, 0, SQLCOM_DDL}
839};
840
841
842struct sa_keyword dml_keywords[]=
843{
844 {2, "DO", 0, SQLCOM_DML},
845 {4, "CALL", 0, SQLCOM_DML},
846 {4, "LOAD", &data_word, SQLCOM_DML},
847 {4, "LOAD", &xml_word, SQLCOM_DML},
848 {6, "DELETE", 0, SQLCOM_DML},
849 {6, "INSERT", 0, SQLCOM_DML},
850 {6, "SELECT", 0, SQLCOM_DML},
851 {6, "UPDATE", 0, SQLCOM_DML},
852 {7, "HANDLER", 0, SQLCOM_DML},
853 {7, "REPLACE", 0, SQLCOM_DML},
854 {0, NULL, 0, SQLCOM_DML}
855};
856
857
858struct sa_keyword dml_no_select_keywords[]=
859{
860 {2, "DO", 0, SQLCOM_DML},
861 {4, "CALL", 0, SQLCOM_DML},
862 {4, "LOAD", &data_word, SQLCOM_DML},
863 {4, "LOAD", &xml_word, SQLCOM_DML},
864 {6, "DELETE", 0, SQLCOM_DML},
865 {6, "INSERT", 0, SQLCOM_DML},
866 {6, "UPDATE", 0, SQLCOM_DML},
867 {7, "HANDLER", 0, SQLCOM_DML},
868 {7, "REPLACE", 0, SQLCOM_DML},
869 {0, NULL, 0, SQLCOM_DML}
870};
871
872
873struct sa_keyword dcl_keywords[]=
874{
875 {6, "CREATE", &user_word, SQLCOM_DCL},
876 {4, "DROP", &user_word, SQLCOM_DCL},
877 {6, "RENAME", &user_word, SQLCOM_DCL},
878 {5, "GRANT", 0, SQLCOM_DCL},
879 {6, "REVOKE", 0, SQLCOM_DCL},
880 {3, "SET", &password_word, SQLCOM_DCL},
881 {0, NULL, 0, SQLCOM_DDL}
882};
883
884
885struct sa_keyword passwd_keywords[]=
886{
887 {3, "SET", &password_word, SQLCOM_SET_OPTION},
888 {5, "ALTER", &server_word, SQLCOM_ALTER_SERVER},
889 {5, "GRANT", 0, SQLCOM_GRANT},
890 {6, "CREATE", &user_word, SQLCOM_CREATE_USER},
891 {6, "CREATE", &server_word, SQLCOM_CREATE_SERVER},
892 {6, "CHANGE", &master_word, SQLCOM_CHANGE_MASTER},
893 {0, NULL, 0, SQLCOM_NOTHING}
894};
895
896#define MAX_KEYWORD 9
897
898
899static void error_header()
900{
901 struct tm tm_time;
902 time_t curtime;
903
904 (void) time(&curtime);
905 (void) localtime_r(&curtime, &tm_time);
906
907 (void) fprintf(stderr,"%02d%02d%02d %2d:%02d:%02d server_audit: ",
908 tm_time.tm_year % 100, tm_time.tm_mon + 1,
909 tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
910}
911
912
913static LOGGER_HANDLE *logfile;
914static struct user_coll incl_user_coll, excl_user_coll;
915static unsigned long long query_counter= 1;
916
917
918static struct connection_info *get_loc_info(MYSQL_THD thd)
919{
920 return (struct connection_info *) THDVAR(thd, loc_info);
921}
922
923
924static int ci_needs_setup(const struct connection_info *ci)
925{
926 return ci->header != 0;
927}
928
929
930static void get_str_n(char *dest, int *dest_len, size_t dest_size,
931 const char *src, size_t src_len)
932{
933 if (src_len >= dest_size)
934 src_len= dest_size - 1;
935
936 memcpy(dest, src, src_len);
937 dest[src_len]= 0;
938 *dest_len= (int)src_len;
939}
940
941
942static int get_user_host(const char *uh_line, unsigned int uh_len,
943 char *buffer, size_t buf_len,
944 size_t *user_len, size_t *host_len, size_t *ip_len)
945{
946 const char *buf_end= buffer + buf_len - 1;
947 const char *buf_start;
948 const char *uh_end= uh_line + uh_len;
949
950 while (uh_line < uh_end && *uh_line != '[')
951 ++uh_line;
952
953 if (uh_line == uh_end)
954 return 1;
955 ++uh_line;
956
957 buf_start= buffer;
958 while (uh_line < uh_end && *uh_line != ']')
959 {
960 if (buffer == buf_end)
961 return 1;
962 *(buffer++)= *(uh_line++);
963 }
964 if (uh_line == uh_end)
965 return 1;
966 *user_len= buffer - buf_start;
967 *(buffer++)= 0;
968
969 while (uh_line < uh_end && *uh_line != '@')
970 ++uh_line;
971 if (uh_line == uh_end || *(++uh_line) == 0)
972 return 1;
973 ++uh_line;
974
975 buf_start= buffer;
976 while (uh_line < uh_end && *uh_line != ' ' && *uh_line != '[')
977 {
978 if (buffer == buf_end)
979 break;
980 *(buffer++)= *(uh_line++);
981 }
982 *host_len= buffer - buf_start;
983 *(buffer++)= 0;
984
985 while (uh_line < uh_end && *uh_line != '[')
986 ++uh_line;
987
988 buf_start= buffer;
989 if (*uh_line == '[')
990 {
991 ++uh_line;
992 while (uh_line < uh_end && *uh_line != ']')
993 *(buffer++)= *(uh_line++);
994 }
995 *ip_len= buffer - buf_start;
996 return 0;
997}
998
999#if defined(__WIN__) && !defined(S_ISDIR)
1000#define S_ISDIR(x) ((x) & _S_IFDIR)
1001#endif /*__WIN__ && !S_ISDIR*/
1002
1003static int start_logging()
1004{
1005 last_error_buf[0]= 0;
1006 log_write_failures= 0;
1007 if (output_type == OUTPUT_FILE)
1008 {
1009 char alt_path_buffer[FN_REFLEN+1+DEFAULT_FILENAME_LEN];
1010 struct stat *f_stat= (struct stat *)alt_path_buffer;
1011 const char *alt_fname= file_path;
1012
1013 while (*alt_fname == ' ')
1014 alt_fname++;
1015
1016 if (*alt_fname == 0)
1017 {
1018 /* Empty string means the default file name. */
1019 alt_fname= default_file_name;
1020 }
1021 else
1022 {
1023 /* See if the directory exists with the name of file_path. */
1024 /* Log file name should be [file_path]/server_audit.log then. */
1025 if (stat(file_path, (struct stat *)alt_path_buffer) == 0 &&
1026 S_ISDIR(f_stat->st_mode))
1027 {
1028 size_t p_len= strlen(file_path);
1029 memcpy(alt_path_buffer, file_path, p_len);
1030 if (alt_path_buffer[p_len-1] != FN_LIBCHAR)
1031 {
1032 alt_path_buffer[p_len]= FN_LIBCHAR;
1033 p_len++;
1034 }
1035 memcpy(alt_path_buffer+p_len, default_file_name, DEFAULT_FILENAME_LEN);
1036 alt_path_buffer[p_len+DEFAULT_FILENAME_LEN]= 0;
1037 alt_fname= alt_path_buffer;
1038 }
1039 }
1040
1041 logfile= logger_open(alt_fname, file_rotate_size, rotations);
1042
1043 if (logfile == NULL)
1044 {
1045 error_header();
1046 fprintf(stderr, "Could not create file '%s'.\n",
1047 alt_fname);
1048 logging= 0;
1049 my_snprintf(last_error_buf, sizeof(last_error_buf),
1050 "Could not create file '%s'.", alt_fname);
1051 is_active= 0;
1052 CLIENT_ERROR(1, "SERVER AUDIT plugin can't create file '%s'.",
1053 MYF(ME_JUST_WARNING), alt_fname);
1054 return 1;
1055 }
1056 error_header();
1057 fprintf(stderr, "logging started to the file %s.\n", alt_fname);
1058 strncpy(current_log_buf, alt_fname, sizeof(current_log_buf));
1059 current_log_buf[sizeof(current_log_buf)-1]= 0;
1060 }
1061 else if (output_type == OUTPUT_SYSLOG)
1062 {
1063 openlog(syslog_ident, LOG_NOWAIT, syslog_facility_codes[syslog_facility]);
1064 error_header();
1065 fprintf(stderr, "logging started to the syslog.\n");
1066 strncpy(current_log_buf, "[SYSLOG]", sizeof(current_log_buf));
1067 }
1068 is_active= 1;
1069 return 0;
1070}
1071
1072
1073static int stop_logging()
1074{
1075 last_error_buf[0]= 0;
1076 if (output_type == OUTPUT_FILE && logfile)
1077 {
1078 logger_close(logfile);
1079 logfile= NULL;
1080 }
1081 else if (output_type == OUTPUT_SYSLOG)
1082 {
1083 closelog();
1084 }
1085 error_header();
1086 fprintf(stderr, "logging was stopped.\n");
1087 is_active= 0;
1088 return 0;
1089}
1090
1091
1092static void setup_connection_simple(struct connection_info *ci)
1093{
1094 ci->db_length= 0;
1095 ci->user_length= 0;
1096 ci->host_length= 0;
1097 ci->ip_length= 0;
1098 ci->query_length= 0;
1099 ci->header= 0;
1100}
1101
1102
1103static void setup_connection_connect(struct connection_info *cn,
1104 const struct mysql_event_connection *event)
1105{
1106 cn->query_id= 0;
1107 cn->query_length= 0;
1108 cn->log_always= 0;
1109 cn->thread_id= event->thread_id;
1110 get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
1111 event->database.str, event->database.length);
1112 get_str_n(cn->user, &cn->user_length, sizeof(cn->db),
1113 event->user, event->user_length);
1114 get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
1115 event->host, event->host_length);
1116 get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
1117 event->ip, event->ip_length);
1118 cn->header= 0;
1119}
1120
1121
1122#define SAFE_STRLEN(s) (s ? strlen(s) : 0)
1123static char empty_str[1]= { 0 };
1124
1125
1126static int is_space(char c)
1127{
1128 return c == ' ' || c == '\r' || c == '\n' || c == '\t';
1129}
1130
1131
1132#define SKIP_SPACES(str) \
1133do { \
1134 while (is_space(*str)) \
1135 ++str; \
1136} while(0)
1137
1138
1139#define ESC_MAP_SIZE 0x60
1140static const char esc_map[ESC_MAP_SIZE]=
1141{
1142 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
1143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1144 0, 0, 0, 0, 0, 0, 0, '\'', 0, 0, 0, 0, 0, 0, 0, 0,
1145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\\', 0, 0, 0
1148};
1149
1150static char escaped_char(char c)
1151{
1152 return ((unsigned char ) c) >= ESC_MAP_SIZE ? 0 : esc_map[(unsigned char) c];
1153}
1154
1155
1156static void setup_connection_initdb(struct connection_info *cn,
1157 const struct mysql_event_general *event)
1158{
1159 size_t user_len, host_len, ip_len;
1160 char uh_buffer[512];
1161
1162 cn->thread_id= event->general_thread_id;
1163 cn->query_id= 0;
1164 cn->query_length= 0;
1165 cn->log_always= 0;
1166 get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
1167 event->general_query, event->general_query_length);
1168
1169 if (get_user_host(event->general_user, event->general_user_length,
1170 uh_buffer, sizeof(uh_buffer),
1171 &user_len, &host_len, &ip_len))
1172 {
1173 /* The user@host line is incorrect. */
1174 cn->user_length= 0;
1175 cn->host_length= 0;
1176 cn->ip_length= 0;
1177 }
1178 else
1179 {
1180 get_str_n(cn->user, &cn->user_length, sizeof(cn->user),
1181 uh_buffer, user_len);
1182 get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
1183 uh_buffer+user_len+1, host_len);
1184 get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
1185 uh_buffer+user_len+1+host_len+1, ip_len);
1186 }
1187 cn->header= 0;
1188}
1189
1190
1191static void setup_connection_table(struct connection_info *cn,
1192 const struct mysql_event_table *event)
1193{
1194 cn->thread_id= event->thread_id;
1195 cn->query_id= query_counter++;
1196 cn->log_always= 0;
1197 cn->query_length= 0;
1198 get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
1199 event->database.str, event->database.length);
1200 get_str_n(cn->user, &cn->user_length, sizeof(cn->db),
1201 event->user, SAFE_STRLEN(event->user));
1202 get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
1203 event->host, SAFE_STRLEN(event->host));
1204 get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
1205 event->ip, SAFE_STRLEN(event->ip));
1206 cn->header= 0;
1207}
1208
1209
1210static void setup_connection_query(struct connection_info *cn,
1211 const struct mysql_event_general *event)
1212{
1213 size_t user_len, host_len, ip_len;
1214 char uh_buffer[512];
1215
1216 cn->thread_id= event->general_thread_id;
1217 cn->query_id= query_counter++;
1218 cn->log_always= 0;
1219 cn->query_length= 0;
1220 get_str_n(cn->db, &cn->db_length, sizeof(cn->db), "", 0);
1221
1222 if (get_user_host(event->general_user, event->general_user_length,
1223 uh_buffer, sizeof(uh_buffer),
1224 &user_len, &host_len, &ip_len))
1225 {
1226 /* The user@host line is incorrect. */
1227 cn->user_length= 0;
1228 cn->host_length= 0;
1229 cn->ip_length= 0;
1230 }
1231 else
1232 {
1233 get_str_n(cn->user, &cn->user_length, sizeof(cn->user),
1234 uh_buffer, user_len);
1235 get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
1236 uh_buffer+user_len+1, host_len);
1237 get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
1238 uh_buffer+user_len+1+host_len+1, ip_len);
1239 }
1240 cn->header= 0;
1241}
1242
1243
1244static void change_connection(struct connection_info *cn,
1245 const struct mysql_event_connection *event)
1246{
1247 get_str_n(cn->user, &cn->user_length, sizeof(cn->user),
1248 event->user, event->user_length);
1249 get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
1250 event->ip, event->ip_length);
1251}
1252
1253static int write_log(const char *message, size_t len)
1254{
1255 if (output_type == OUTPUT_FILE)
1256 {
1257 if (logfile &&
1258 (is_active= (logger_write(logfile, message, len) == (int)len)))
1259 return 0;
1260 ++log_write_failures;
1261 return 1;
1262 }
1263 else if (output_type == OUTPUT_SYSLOG)
1264 {
1265 syslog(syslog_facility_codes[syslog_facility] |
1266 syslog_priority_codes[syslog_priority],
1267 "%s %.*s", syslog_info, (int)len, message);
1268 }
1269 return 0;
1270}
1271
1272
1273static size_t log_header(char *message, size_t message_len,
1274 time_t *ts,
1275 const char *serverhost, unsigned int serverhost_len,
1276 const char *username, unsigned int username_len,
1277 const char *host, unsigned int host_len,
1278 const char *userip, unsigned int userip_len,
1279 unsigned int connection_id, long long query_id,
1280 const char *operation)
1281{
1282 struct tm tm_time;
1283
1284 if (host_len == 0 && userip_len != 0)
1285 {
1286 host_len= userip_len;
1287 host= userip;
1288 }
1289
1290 if (output_type == OUTPUT_SYSLOG)
1291 return my_snprintf(message, message_len,
1292 "%.*s,%.*s,%.*s,%d,%lld,%s",
1293 serverhost_len, serverhost,
1294 username_len, username,
1295 host_len, host,
1296 connection_id, query_id, operation);
1297
1298 (void) localtime_r(ts, &tm_time);
1299 return my_snprintf(message, message_len,
1300 "%04d%02d%02d %02d:%02d:%02d,%.*s,%.*s,%.*s,%d,%lld,%s",
1301 tm_time.tm_year+1900, tm_time.tm_mon+1, tm_time.tm_mday,
1302 tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,
1303 serverhost_len, serverhost,
1304 username_len, username,
1305 host_len, host,
1306 connection_id, query_id, operation);
1307}
1308
1309
1310static int log_connection(const struct connection_info *cn,
1311 const struct mysql_event_connection *event,
1312 const char *type)
1313{
1314 time_t ctime;
1315 size_t csize;
1316 char message[1024];
1317
1318 (void) time(&ctime);
1319 csize= log_header(message, sizeof(message)-1, &ctime,
1320 servhost, servhost_len,
1321 cn->user, cn->user_length,
1322 cn->host, cn->host_length,
1323 cn->ip, cn->ip_length,
1324 event->thread_id, 0, type);
1325 csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
1326 ",%.*s,,%d", cn->db_length, cn->db, event->status);
1327 message[csize]= '\n';
1328 return write_log(message, csize + 1);
1329}
1330
1331
1332static int log_connection_event(const struct mysql_event_connection *event,
1333 const char *type)
1334{
1335 time_t ctime;
1336 size_t csize;
1337 char message[1024];
1338
1339 (void) time(&ctime);
1340 csize= log_header(message, sizeof(message)-1, &ctime,
1341 servhost, servhost_len,
1342 event->user, event->user_length,
1343 event->host, event->host_length,
1344 event->ip, event->ip_length,
1345 event->thread_id, 0, type);
1346 csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
1347 ",%.*s,,%d", event->database.length, event->database.str, event->status);
1348 message[csize]= '\n';
1349 return write_log(message, csize + 1);
1350}
1351
1352
1353static size_t escape_string(const char *str, unsigned int len,
1354 char *result, size_t result_len)
1355{
1356 const char *res_start= result;
1357 const char *res_end= result + result_len - 2;
1358 while (len)
1359 {
1360 char esc_c;
1361
1362 if (result >= res_end)
1363 break;
1364 if ((esc_c= escaped_char(*str)))
1365 {
1366 if (result+1 >= res_end)
1367 break;
1368 *(result++)= '\\';
1369 *(result++)= esc_c;
1370 }
1371 else if (is_space(*str))
1372 *(result++)= ' ';
1373 else
1374 *(result++)= *str;
1375 str++;
1376 len--;
1377 }
1378 *result= 0;
1379 return result - res_start;
1380}
1381
1382
1383static size_t escape_string_hide_passwords(const char *str, unsigned int len,
1384 char *result, size_t result_len,
1385 const char *word1, size_t word1_len,
1386 const char *word2, size_t word2_len,
1387 int next_text_string)
1388{
1389 const char *res_start= result;
1390 const char *res_end= result + result_len - 2;
1391 size_t d_len;
1392 char b_char;
1393
1394 while (len)
1395 {
1396 if (len > word1_len + 1 && strncasecmp(str, word1, word1_len) == 0)
1397 {
1398 const char *next_s= str + word1_len;
1399 size_t c;
1400
1401 if (next_text_string)
1402 {
1403 while (*next_s && *next_s != '\'' && *next_s != '"')
1404 ++next_s;
1405 }
1406 else
1407 {
1408 if (word2)
1409 {
1410 SKIP_SPACES(next_s);
1411 if (len < (next_s - str) + word2_len + 1 ||
1412 strncasecmp(next_s, word2, word2_len) != 0)
1413 goto no_password;
1414 next_s+= word2_len;
1415 }
1416
1417 while (*next_s && *next_s != '\'' && *next_s != '"')
1418 ++next_s;
1419 }
1420
1421 d_len= next_s - str;
1422 if (result + d_len + 5 > res_end)
1423 break;
1424
1425 for (c=0; c<d_len; c++)
1426 result[c]= is_space(str[c]) ? ' ' : str[c];
1427
1428 if (*next_s)
1429 {
1430 memmove(result + d_len, "*****", 5);
1431 result+= d_len + 5;
1432 b_char= *(next_s++);
1433 }
1434 else
1435 result+= d_len;
1436
1437 while (*next_s)
1438 {
1439 if (*next_s == b_char)
1440 {
1441 ++next_s;
1442 break;
1443 }
1444 if (*next_s == '\\')
1445 {
1446 if (next_s[1])
1447 next_s++;
1448 }
1449 next_s++;
1450 }
1451 len-= (uint)(next_s - str);
1452 str= next_s;
1453 continue;
1454 }
1455no_password:
1456 if (result >= res_end)
1457 break;
1458 if ((b_char= escaped_char(*str)))
1459 {
1460 if (result+1 >= res_end)
1461 break;
1462 *(result++)= '\\';
1463 *(result++)= b_char;
1464 }
1465 else if (is_space(*str))
1466 *(result++)= ' ';
1467 else
1468 *(result++)= *str;
1469 str++;
1470 len--;
1471 }
1472 *result= 0;
1473 return result - res_start;
1474}
1475
1476
1477
1478static int do_log_user(const char *name)
1479{
1480 size_t len;
1481
1482 if (!name)
1483 return 0;
1484 len= strlen(name);
1485
1486 if (incl_user_coll.n_users)
1487 return coll_search(&incl_user_coll, name, len) != 0;
1488
1489 if (excl_user_coll.n_users)
1490 return coll_search(&excl_user_coll, name, len) == 0;
1491
1492 return 1;
1493}
1494
1495
1496static int get_next_word(const char *query, char *word)
1497{
1498 int len= 0;
1499 char c;
1500 while ((c= query[len]))
1501 {
1502 if (c >= 'a' && c <= 'z')
1503 word[len]= 'A' + (c-'a');
1504 else if (c >= 'A' && c <= 'Z')
1505 word[len]= c;
1506 else
1507 break;
1508
1509 if (len++ == MAX_KEYWORD)
1510 return 0;
1511 }
1512 word[len]= 0;
1513 return len;
1514}
1515
1516
1517static int filter_query_type(const char *query, struct sa_keyword *kwd)
1518{
1519 int qwe_in_list;
1520 char fword[MAX_KEYWORD + 1], nword[MAX_KEYWORD + 1];
1521 int len, nlen= 0;
1522 const struct sa_keyword *l_keywords;
1523
1524 while (*query && (is_space(*query) || *query == '(' || *query == '/'))
1525 {
1526 /* comment handling */
1527 if (*query == '/' && query[1] == '*')
1528 {
1529 if (query[2] == '!')
1530 {
1531 query+= 3;
1532 while (*query >= '0' && *query <= '9')
1533 query++;
1534 continue;
1535 }
1536 query+= 2;
1537 while (*query)
1538 {
1539 if (*query=='*' && query[1] == '/')
1540 {
1541 query+= 2;
1542 break;
1543 }
1544 query++;
1545 }
1546 continue;
1547 }
1548 query++;
1549 }
1550
1551 qwe_in_list= 0;
1552 if (!(len= get_next_word(query, fword)))
1553 goto not_in_list;
1554 query+= len+1;
1555
1556 l_keywords= kwd;
1557 while (l_keywords->length)
1558 {
1559 if (l_keywords->length == len && strncmp(l_keywords->wd, fword, len) == 0)
1560 {
1561 if (l_keywords->next)
1562 {
1563 if (nlen == 0)
1564 {
1565 while (*query && is_space(*query))
1566 query++;
1567 nlen= get_next_word(query, nword);
1568 }
1569 if (l_keywords->next->length != nlen ||
1570 strncmp(l_keywords->next->wd, nword, nlen) != 0)
1571 goto do_loop;
1572 }
1573
1574 qwe_in_list= l_keywords->type;
1575 break;
1576 };
1577do_loop:
1578 l_keywords++;
1579 }
1580
1581not_in_list:
1582 return qwe_in_list;
1583}
1584
1585
1586static int log_statement_ex(const struct connection_info *cn,
1587 time_t ev_time, unsigned long thd_id,
1588 const char *query, unsigned int query_len,
1589 int error_code, const char *type)
1590{
1591 size_t csize;
1592 char message_loc[1024];
1593 char *message= message_loc;
1594 size_t message_size= sizeof(message_loc);
1595 char *uh_buffer;
1596 size_t uh_buffer_size;
1597 const char *db;
1598 unsigned int db_length;
1599 long long query_id;
1600 int result;
1601
1602 if ((db= cn->db))
1603 db_length= cn->db_length;
1604 else
1605 {
1606 db= "";
1607 db_length= 0;
1608 }
1609
1610 if (!(query_id= cn->query_id))
1611 query_id= query_counter++;
1612
1613 if (query == 0)
1614 {
1615 /* Can happen after the error in mysqld_prepare_stmt() */
1616 query= cn->query;
1617 query_len= cn->query_length;
1618 if (query == 0 || query_len == 0)
1619 return 0;
1620 }
1621
1622 if (query && !(events & EVENT_QUERY_ALL) &&
1623 (events & EVENT_QUERY))
1624 {
1625 const char *orig_query= query;
1626
1627 if (filter_query_type(query, keywords_to_skip))
1628 {
1629 char fword[MAX_KEYWORD + 1];
1630 int len;
1631 do
1632 {
1633 len= get_next_word(query, fword);
1634 query+= len ? len : 1;
1635 if (len == 3 && strncmp(fword, "FOR", 3) == 0)
1636 break;
1637 } while (*query);
1638
1639 if (*query == 0)
1640 return 0;
1641 }
1642
1643 if (events & EVENT_QUERY_DDL)
1644 {
1645 if (!filter_query_type(query, not_ddl_keywords) &&
1646 filter_query_type(query, ddl_keywords))
1647 goto do_log_query;
1648 }
1649 if (events & EVENT_QUERY_DML)
1650 {
1651 if (filter_query_type(query, dml_keywords))
1652 goto do_log_query;
1653 }
1654 if (events & EVENT_QUERY_DML_NO_SELECT)
1655 {
1656 if (filter_query_type(query, dml_no_select_keywords))
1657 goto do_log_query;
1658 }
1659 if (events & EVENT_QUERY_DCL)
1660 {
1661 if (filter_query_type(query, dcl_keywords))
1662 goto do_log_query;
1663 }
1664
1665 return 0;
1666do_log_query:
1667 query= orig_query;
1668 }
1669
1670 csize= log_header(message, message_size-1, &ev_time,
1671 servhost, servhost_len,
1672 cn->user, cn->user_length,cn->host, cn->host_length,
1673 cn->ip, cn->ip_length, thd_id, query_id, type);
1674
1675 csize+= my_snprintf(message+csize, message_size - 1 - csize,
1676 ",%.*s,\'", db_length, db);
1677
1678 if (query_log_limit > 0 && query_len > query_log_limit)
1679 query_len= query_log_limit;
1680
1681 if (query_len > (message_size - csize)/2)
1682 {
1683 flogger_mutex_lock(&lock_bigbuffer);
1684 if (big_buffer_alloced < (query_len * 2 + csize))
1685 {
1686 big_buffer_alloced= (query_len * 2 + csize + 4095) & ~4095L;
1687 big_buffer= realloc(big_buffer, big_buffer_alloced);
1688 if (big_buffer == NULL)
1689 {
1690 big_buffer_alloced= 0;
1691 return 0;
1692 }
1693 }
1694
1695 memcpy(big_buffer, message, csize);
1696 message= big_buffer;
1697 message_size= big_buffer_alloced;
1698 }
1699
1700 uh_buffer= message + csize;
1701 uh_buffer_size= message_size - csize;
1702 if (query_log_limit > 0 && uh_buffer_size > query_log_limit+2)
1703 uh_buffer_size= query_log_limit+2;
1704
1705 switch (filter_query_type(query, passwd_keywords))
1706 {
1707 case SQLCOM_GRANT:
1708 case SQLCOM_CREATE_USER:
1709 csize+= escape_string_hide_passwords(query, query_len,
1710 uh_buffer, uh_buffer_size,
1711 "IDENTIFIED", 10, "BY", 2, 0);
1712 break;
1713 case SQLCOM_CHANGE_MASTER:
1714 csize+= escape_string_hide_passwords(query, query_len,
1715 uh_buffer, uh_buffer_size,
1716 "MASTER_PASSWORD", 15, "=", 1, 0);
1717 break;
1718 case SQLCOM_CREATE_SERVER:
1719 case SQLCOM_ALTER_SERVER:
1720 csize+= escape_string_hide_passwords(query, query_len,
1721 uh_buffer, uh_buffer_size,
1722 "PASSWORD", 8, NULL, 0, 0);
1723 break;
1724 case SQLCOM_SET_OPTION:
1725 csize+= escape_string_hide_passwords(query, query_len,
1726 uh_buffer, uh_buffer_size,
1727 "=", 1, NULL, 0, 1);
1728 break;
1729 default:
1730 csize+= escape_string(query, query_len,
1731 uh_buffer, uh_buffer_size);
1732 break;
1733 }
1734 csize+= my_snprintf(message+csize, message_size - 1 - csize,
1735 "\',%d", error_code);
1736 message[csize]= '\n';
1737 result= write_log(message, csize + 1);
1738 if (message == big_buffer)
1739 flogger_mutex_unlock(&lock_bigbuffer);
1740
1741 return result;
1742}
1743
1744
1745static int log_statement(const struct connection_info *cn,
1746 const struct mysql_event_general *event,
1747 const char *type)
1748{
1749 return log_statement_ex(cn, event->general_time, event->general_thread_id,
1750 event->general_query, event->general_query_length,
1751 event->general_error_code, type);
1752}
1753
1754
1755static int log_table(const struct connection_info *cn,
1756 const struct mysql_event_table *event, const char *type)
1757{
1758 size_t csize;
1759 char message[1024];
1760 time_t ctime;
1761
1762 (void) time(&ctime);
1763 csize= log_header(message, sizeof(message)-1, &ctime,
1764 servhost, servhost_len,
1765 event->user, (unsigned int)SAFE_STRLEN(event->user),
1766 event->host, (unsigned int)SAFE_STRLEN(event->host),
1767 event->ip, (unsigned int)SAFE_STRLEN(event->ip),
1768 event->thread_id, cn->query_id, type);
1769 csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
1770 ",%.*s,%.*s,",event->database.length, event->database.str,
1771 event->table.length, event->table.str);
1772 message[csize]= '\n';
1773 return write_log(message, csize + 1);
1774}
1775
1776
1777static int log_rename(const struct connection_info *cn,
1778 const struct mysql_event_table *event)
1779{
1780 size_t csize;
1781 char message[1024];
1782 time_t ctime;
1783
1784 (void) time(&ctime);
1785 csize= log_header(message, sizeof(message)-1, &ctime,
1786 servhost, servhost_len,
1787 event->user, (unsigned int)SAFE_STRLEN(event->user),
1788 event->host, (unsigned int)SAFE_STRLEN(event->host),
1789 event->ip, (unsigned int)SAFE_STRLEN(event->ip),
1790 event->thread_id, cn->query_id, "RENAME");
1791 csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
1792 ",%.*s,%.*s|%.*s.%.*s,",event->database.length, event->database.str,
1793 event->table.length, event->table.str,
1794 event->new_database.length, event->new_database.str,
1795 event->new_table.length, event->new_table.str);
1796 message[csize]= '\n';
1797 return write_log(message, csize + 1);
1798}
1799
1800
1801static int event_query_command(const struct mysql_event_general *event)
1802{
1803 return (event->general_command_length == 5 &&
1804 strncmp(event->general_command, "Query", 5) == 0) ||
1805 (event->general_command_length == 7 &&
1806 (strncmp(event->general_command, "Execute", 7) == 0 ||
1807 (event->general_error_code != 0 &&
1808 strncmp(event->general_command, "Prepare", 7) == 0)));
1809}
1810
1811
1812static void update_general_user(struct connection_info *cn,
1813 const struct mysql_event_general *event)
1814{
1815 char uh_buffer[768];
1816 size_t user_len, host_len, ip_len;
1817 if (cn->user_length == 0 && cn->host_length == 0 && cn->ip_length == 0 &&
1818 get_user_host(event->general_user, event->general_user_length,
1819 uh_buffer, sizeof(uh_buffer),
1820 &user_len, &host_len, &ip_len) == 0)
1821 {
1822 get_str_n(cn->user, &cn->user_length, sizeof(cn->user),
1823 uh_buffer, user_len);
1824 get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
1825 uh_buffer+user_len+1, host_len);
1826 get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
1827 uh_buffer+user_len+1+host_len+1, ip_len);
1828 }
1829
1830}
1831
1832
1833static struct connection_info ci_disconnect_buffer;
1834
1835#define AA_FREE_CONNECTION 1
1836#define AA_CHANGE_USER 2
1837
1838static void update_connection_info(struct connection_info *cn,
1839 unsigned int event_class, const void *ev, int *after_action)
1840{
1841 *after_action= 0;
1842
1843 switch (event_class) {
1844 case MYSQL_AUDIT_GENERAL_CLASS:
1845 {
1846 const struct mysql_event_general *event =
1847 (const struct mysql_event_general *) ev;
1848 switch (event->event_subclass) {
1849 case MYSQL_AUDIT_GENERAL_LOG:
1850 {
1851 int init_db_command= event->general_command_length == 7 &&
1852 strncmp(event->general_command, "Init DB", 7) == 0;
1853 if (!ci_needs_setup(cn))
1854 {
1855 if (init_db_command)
1856 {
1857 /* Change DB */
1858 if (mysql_57_started)
1859 get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
1860 event->database.str, event->database.length);
1861 else
1862 get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
1863 event->general_query, event->general_query_length);
1864 }
1865 cn->query_id= mode ? query_counter++ : event->query_id;
1866 cn->query= event->general_query;
1867 cn->query_length= event->general_query_length;
1868 cn->query_time= (time_t) event->general_time;
1869 update_general_user(cn, event);
1870 }
1871 else if (init_db_command)
1872 setup_connection_initdb(cn, event);
1873 else if (event_query_command(event))
1874 setup_connection_query(cn, event);
1875 else
1876 setup_connection_simple(cn);
1877 break;
1878 }
1879
1880 case MYSQL_AUDIT_GENERAL_STATUS:
1881 if (event_query_command(event))
1882 {
1883 if (ci_needs_setup(cn))
1884 setup_connection_query(cn, event);
1885
1886 if (mode == 0 && cn->db_length == 0 && event->database.length > 0)
1887 get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
1888 event->database.str, event->database.length);
1889
1890 if (event->general_error_code == 0)
1891 {
1892 /* We need to check if it's the USE command to change the DB */
1893 int use_command= event->general_query_length > 4 &&
1894 strncasecmp(event->general_query, "use ", 4) == 0;
1895 if (use_command)
1896 {
1897 /* Change DB */
1898 if (mode)
1899 get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
1900 event->general_query + 4, event->general_query_length - 4);
1901 else
1902 get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
1903 event->database.str, event->database.length);
1904 }
1905 }
1906 update_general_user(cn, event);
1907 }
1908 break;
1909 case MYSQL_AUDIT_GENERAL_ERROR:
1910 /*
1911 We need this because the MariaDB returns NULL query field for the
1912 MYSQL_AUDIT_GENERAL_STATUS in the mysqld_stmt_prepare.
1913 As a result we get empty QUERY field for errors.
1914 */
1915 if (ci_needs_setup(cn))
1916 setup_connection_query(cn, event);
1917 cn->query_id= mode ? query_counter++ : event->query_id;
1918 get_str_n(cn->query_buffer, &cn->query_length, sizeof(cn->query_buffer),
1919 event->general_query, event->general_query_length);
1920 cn->query= cn->query_buffer;
1921 cn->query_time= (time_t) event->general_time;
1922 break;
1923 default:;
1924 }
1925 break;
1926 }
1927 case MYSQL_AUDIT_TABLE_CLASS:
1928 {
1929 const struct mysql_event_table *event =
1930 (const struct mysql_event_table *) ev;
1931 if (ci_needs_setup(cn))
1932 setup_connection_table(cn, event);
1933
1934 if (cn->user_length == 0 && cn->host_length == 0 && cn->ip_length == 0)
1935 {
1936 get_str_n(cn->user, &cn->user_length, sizeof(cn->user),
1937 event->user, SAFE_STRLEN(event->user));
1938 get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
1939 event->host, SAFE_STRLEN(event->host));
1940 get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
1941 event->ip, SAFE_STRLEN(event->ip));
1942 }
1943
1944 if (cn->db_length == 0 && event->database.length != 0)
1945 get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
1946 event->database.str, event->database.length);
1947
1948 if (mode == 0)
1949 cn->query_id= event->query_id;
1950 break;
1951 }
1952 case MYSQL_AUDIT_CONNECTION_CLASS:
1953 {
1954 const struct mysql_event_connection *event =
1955 (const struct mysql_event_connection *) ev;
1956 switch (event->event_subclass)
1957 {
1958 case MYSQL_AUDIT_CONNECTION_CONNECT:
1959 setup_connection_connect(cn, event);
1960 break;
1961 case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
1962 *after_action= AA_CHANGE_USER;
1963 break;
1964 default:;
1965 }
1966 break;
1967 }
1968 default:
1969 break;
1970 }
1971}
1972
1973
1974struct connection_info cn_error_buffer;
1975
1976
1977#define FILTER(MASK) (events == 0 || (events & MASK))
1978void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
1979{
1980 struct connection_info *cn= 0;
1981 int after_action= 0;
1982
1983 /* That one is important as this function can be called with */
1984 /* &lock_operations locked when the server logs an error reported */
1985 /* by this plugin. */
1986 if (!thd || internal_stop_logging)
1987 return;
1988
1989 flogger_mutex_lock(&lock_operations);
1990
1991 if (maria_55_started && debug_server_started &&
1992 event_class == MYSQL_AUDIT_GENERAL_CLASS)
1993 {
1994 /*
1995 There's a bug in MariaDB 5.5 that prevents using thread local
1996 variables in some cases.
1997 The 'select * from notexisting_table;' query produces such case.
1998 So just use the static buffer in this case.
1999 */
2000 const struct mysql_event_general *event =
2001 (const struct mysql_event_general *) ev;
2002
2003 if (event->event_subclass == MYSQL_AUDIT_GENERAL_ERROR ||
2004 (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS &&
2005 event->general_query_length == 0 &&
2006 cn_error_buffer.query_id == event->query_id))
2007 {
2008 cn= &cn_error_buffer;
2009 cn->header= 1;
2010 }
2011 else
2012 cn= get_loc_info(thd);
2013 }
2014 else
2015 {
2016 cn= get_loc_info(thd);
2017 }
2018
2019 update_connection_info(cn, event_class, ev, &after_action);
2020
2021 if (!logging)
2022 goto exit_func;
2023
2024 if (event_class == MYSQL_AUDIT_GENERAL_CLASS && FILTER(EVENT_QUERY) &&
2025 cn && do_log_user(cn->user))
2026 {
2027 const struct mysql_event_general *event =
2028 (const struct mysql_event_general *) ev;
2029
2030 /*
2031 Only one subclass is logged.
2032 */
2033 if (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS &&
2034 event_query_command(event))
2035 {
2036 log_statement(cn, event, "QUERY");
2037 cn->query_length= 0; /* So the log_current_query() won't log this again. */
2038 }
2039 }
2040 else if (event_class == MYSQL_AUDIT_TABLE_CLASS && FILTER(EVENT_TABLE) && cn)
2041 {
2042 const struct mysql_event_table *event =
2043 (const struct mysql_event_table *) ev;
2044 if (do_log_user(event->user))
2045 {
2046 switch (event->event_subclass)
2047 {
2048 case MYSQL_AUDIT_TABLE_LOCK:
2049 log_table(cn, event, event->read_only ? "READ" : "WRITE");
2050 break;
2051 case MYSQL_AUDIT_TABLE_CREATE:
2052 log_table(cn, event, "CREATE");
2053 break;
2054 case MYSQL_AUDIT_TABLE_DROP:
2055 log_table(cn, event, "DROP");
2056 break;
2057 case MYSQL_AUDIT_TABLE_RENAME:
2058 log_rename(cn, event);
2059 break;
2060 case MYSQL_AUDIT_TABLE_ALTER:
2061 log_table(cn, event, "ALTER");
2062 break;
2063 default:
2064 break;
2065 }
2066 }
2067 }
2068 else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS &&
2069 FILTER(EVENT_CONNECT) && cn)
2070 {
2071 const struct mysql_event_connection *event =
2072 (const struct mysql_event_connection *) ev;
2073 switch (event->event_subclass)
2074 {
2075 case MYSQL_AUDIT_CONNECTION_CONNECT:
2076 log_connection(cn, event, event->status ? "FAILED_CONNECT": "CONNECT");
2077 break;
2078 case MYSQL_AUDIT_CONNECTION_DISCONNECT:
2079 if (use_event_data_for_disconnect)
2080 log_connection_event(event, "DISCONNECT");
2081 else
2082 log_connection(&ci_disconnect_buffer, event, "DISCONNECT");
2083 break;
2084 case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
2085 log_connection(cn, event, "CHANGEUSER");
2086 break;
2087 default:;
2088 }
2089 }
2090exit_func:
2091 /*
2092 This must work always, whether logging is ON or not.
2093 */
2094 if (after_action)
2095 {
2096 switch (after_action) {
2097 case AA_CHANGE_USER:
2098 {
2099 const struct mysql_event_connection *event =
2100 (const struct mysql_event_connection *) ev;
2101 change_connection(cn, event);
2102 break;
2103 }
2104 default:
2105 break;
2106 }
2107 }
2108 if (cn)
2109 cn->log_always= 0;
2110 flogger_mutex_unlock(&lock_operations);
2111}
2112
2113
2114struct mysql_event_general_v8
2115{
2116 unsigned int event_class;
2117 unsigned int event_subclass;
2118 int general_error_code;
2119 unsigned long general_thread_id;
2120 const char *general_user;
2121 unsigned int general_user_length;
2122 const char *general_command;
2123 unsigned int general_command_length;
2124 const char *general_query;
2125 unsigned int general_query_length;
2126 struct charset_info_st *general_charset;
2127 unsigned long long general_time;
2128 unsigned long long general_rows;
2129};
2130
2131
2132static void auditing_v8(MYSQL_THD thd, struct mysql_event_general_v8 *ev_v8)
2133{
2134#ifdef __linux__
2135#ifdef DBUG_OFF
2136 #ifdef __x86_64__
2137 static const int cmd_off= 4200;
2138 static const int db_off= 120;
2139 static const int db_len_off= 128;
2140 #else
2141 static const int cmd_off= 2668;
2142 static const int db_off= 60;
2143 static const int db_len_off= 64;
2144 #endif /*x86_64*/
2145#else
2146 #ifdef __x86_64__
2147 static const int cmd_off= 4432;
2148 static const int db_off= 120;
2149 static const int db_len_off= 128;
2150 #else
2151 static const int cmd_off= 2808;
2152 static const int db_off= 64;
2153 static const int db_len_off= 68;
2154 #endif /*x86_64*/
2155#endif /*DBUG_OFF*/
2156#endif /* __linux__ */
2157 struct mysql_event_general event;
2158
2159 if (ev_v8->event_class != MYSQL_AUDIT_GENERAL_CLASS)
2160 return;
2161
2162 event.event_subclass= ev_v8->event_subclass;
2163 event.general_error_code= ev_v8->general_error_code;
2164 event.general_thread_id= ev_v8->general_thread_id;
2165 event.general_user= ev_v8->general_user;
2166 event.general_user_length= ev_v8->general_user_length;
2167 event.general_command= ev_v8->general_command;
2168 event.general_command_length= ev_v8->general_command_length;
2169 event.general_query= ev_v8->general_query;
2170 event.general_query_length= ev_v8->general_query_length;
2171 event.general_charset= ev_v8->general_charset;
2172 event.general_time= ev_v8->general_time;
2173 event.general_rows= ev_v8->general_rows;
2174 event.database.str= 0;
2175 event.database.length= 0;
2176
2177 if (event.general_query_length > 0)
2178 {
2179 event.event_subclass= MYSQL_AUDIT_GENERAL_STATUS;
2180 event.general_command= "Query";
2181 event.general_command_length= 5;
2182#ifdef __linux__
2183 event.database.str= *(char **) (((char *) thd) + db_off);
2184 event.database.length= *(size_t *) (((char *) thd) + db_len_off);
2185#endif /*__linux*/
2186 }
2187#ifdef __linux__
2188 else if (*((int *) (((char *)thd) + cmd_off)) == 2)
2189 {
2190 event.event_subclass= MYSQL_AUDIT_GENERAL_LOG;
2191 event.general_command= "Init DB";
2192 event.general_command_length= 7;
2193 event.general_query= *(char **) (((char *) thd) + db_off);
2194 event.general_query_length= *(size_t *) (((char *) thd) + db_len_off);
2195 }
2196#endif /*__linux*/
2197 auditing(thd, ev_v8->event_class, &event);
2198}
2199
2200
2201static void auditing_v13(MYSQL_THD thd, unsigned int *ev_v0)
2202{
2203 struct mysql_event_general event= *(const struct mysql_event_general *) (ev_v0+1);
2204
2205 if (event.general_query_length > 0)
2206 {
2207 event.event_subclass= MYSQL_AUDIT_GENERAL_STATUS;
2208 event.general_command= "Query";
2209 event.general_command_length= 5;
2210 }
2211 auditing(thd, ev_v0[0], &event);
2212}
2213
2214
2215int get_db_mysql57(MYSQL_THD thd, char **name, int *len)
2216{
2217 int db_off;
2218 int db_len_off;
2219 if (debug_server_started)
2220 {
2221#ifdef __x86_64__
2222 db_off= 608;
2223 db_len_off= 616;
2224#else
2225 db_off= 0;
2226 db_len_off= 0;
2227#endif /*x86_64*/
2228 }
2229 else
2230 {
2231#ifdef __x86_64__
2232 db_off= 536;
2233 db_len_off= 544;
2234#else
2235 db_off= 0;
2236 db_len_off= 0;
2237#endif /*x86_64*/
2238 }
2239
2240#ifdef __linux__
2241 *name= *(char **) (((char *) thd) + db_off);
2242 *len= *((int *) (((char*) thd) + db_len_off));
2243 if (*name && (*name)[*len] != 0)
2244 return 1;
2245 return 0;
2246#else
2247 return 1;
2248#endif
2249}
2250/*
2251 As it's just too difficult to #include "sql_class.h",
2252 let's just copy the necessary part of the system_variables
2253 structure here.
2254*/
2255typedef struct loc_system_variables
2256{
2257 ulong dynamic_variables_version;
2258 char* dynamic_variables_ptr;
2259 uint dynamic_variables_head; /* largest valid variable offset */
2260 uint dynamic_variables_size; /* how many bytes are in use */
2261
2262 ulonglong max_heap_table_size;
2263 ulonglong tmp_table_size;
2264 ulonglong long_query_time;
2265 ulonglong optimizer_switch;
2266 ulonglong sql_mode; ///< which non-standard SQL behaviour should be enabled
2267 ulonglong option_bits; ///< OPTION_xxx constants, e.g. OPTION_PROFILING
2268 ulonglong join_buff_space_limit;
2269 ulonglong log_slow_filter;
2270 ulonglong log_slow_verbosity;
2271 ulonglong bulk_insert_buff_size;
2272 ulonglong join_buff_size;
2273 ulonglong sortbuff_size;
2274 ulonglong group_concat_max_len;
2275 ha_rows select_limit;
2276 ha_rows max_join_size;
2277 ha_rows expensive_subquery_limit;
2278 ulong auto_increment_increment, auto_increment_offset;
2279 ulong lock_wait_timeout;
2280 ulong join_cache_level;
2281 ulong max_allowed_packet;
2282 ulong max_error_count;
2283 ulong max_length_for_sort_data;
2284 ulong max_sort_length;
2285 ulong max_tmp_tables;
2286 ulong max_insert_delayed_threads;
2287 ulong min_examined_row_limit;
2288 ulong multi_range_count;
2289 ulong net_buffer_length;
2290 ulong net_interactive_timeout;
2291 ulong net_read_timeout;
2292 ulong net_retry_count;
2293 ulong net_wait_timeout;
2294 ulong net_write_timeout;
2295 ulong optimizer_prune_level;
2296 ulong optimizer_search_depth;
2297 ulong preload_buff_size;
2298 ulong profiling_history_size;
2299 ulong read_buff_size;
2300 ulong read_rnd_buff_size;
2301 ulong mrr_buff_size;
2302 ulong div_precincrement;
2303 /* Total size of all buffers used by the subselect_rowid_merge_engine. */
2304 ulong rowid_merge_buff_size;
2305 ulong max_sp_recursion_depth;
2306 ulong default_week_format;
2307 ulong max_seeks_for_key;
2308 ulong range_alloc_block_size;
2309 ulong query_alloc_block_size;
2310 ulong query_prealloc_size;
2311 ulong trans_alloc_block_size;
2312 ulong trans_prealloc_size;
2313 ulong log_warnings;
2314 /* Flags for slow log filtering */
2315 ulong log_slow_rate_limit;
2316 ulong binlog_format; ///< binlog format for this thd (see enum_binlog_format)
2317 ulong progress_report_time;
2318 my_bool binlog_annotate_row_events;
2319 my_bool binlog_direct_non_trans_update;
2320 my_bool sql_log_bin;
2321 ulong completion_type;
2322 ulong query_cache_type;
2323} LOC_SV;
2324
2325
2326static int init_done= 0;
2327
2328static int server_audit_init(void *p __attribute__((unused)))
2329{
2330 if (!serv_ver)
2331 {
2332#ifdef _WIN32
2333 serv_ver= (const char *) GetProcAddress(0, "server_version");
2334#else
2335 serv_ver= server_version;
2336#endif /*_WIN32*/
2337 }
2338 if (!mysql_57_started)
2339 {
2340 const void *my_hash_init_ptr= dlsym(RTLD_DEFAULT, "_my_hash_init");
2341 if (!my_hash_init_ptr)
2342 {
2343 maria_above_5= 1;
2344 my_hash_init_ptr= dlsym(RTLD_DEFAULT, "my_hash_init2");
2345 }
2346 if (!my_hash_init_ptr)
2347 return 1;
2348 }
2349
2350 if(!(int_mysql_data_home= dlsym(RTLD_DEFAULT, "mysql_data_home")))
2351 {
2352 if(!(int_mysql_data_home= dlsym(RTLD_DEFAULT, "?mysql_data_home@@3PADA")))
2353 int_mysql_data_home= &default_home;
2354 }
2355
2356 if (!serv_ver)
2357 return 1;
2358
2359 if (!started_mysql)
2360 {
2361 if (!maria_above_5 && serv_ver[4]=='3' && serv_ver[5]<'3')
2362 {
2363 mode= 1;
2364 mode_readonly= 1;
2365 }
2366 }
2367
2368 if (gethostname(servhost, sizeof(servhost)))
2369 strcpy(servhost, "unknown");
2370
2371 servhost_len= (uint)strlen(servhost);
2372
2373 logger_init_mutexes();
2374#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI)
2375 if (PSI_server)
2376 PSI_server->register_mutex("server_audit", mutex_key_list, 1);
2377#endif
2378 flogger_mutex_init(key_LOCK_operations, &lock_operations, MY_MUTEX_INIT_FAST);
2379 flogger_mutex_init(key_LOCK_operations, &lock_bigbuffer, MY_MUTEX_INIT_FAST);
2380
2381 coll_init(&incl_user_coll);
2382 coll_init(&excl_user_coll);
2383
2384 if (incl_users)
2385 {
2386 if (excl_users)
2387 {
2388 incl_users= excl_users= NULL;
2389 error_header();
2390 fprintf(stderr, "INCL_DML_USERS and EXCL_DML_USERS specified"
2391 " simultaneously - both set to empty\n");
2392 }
2393 update_incl_users(NULL, NULL, NULL, &incl_users);
2394 }
2395 else if (excl_users)
2396 {
2397 update_excl_users(NULL, NULL, NULL, &excl_users);
2398 }
2399
2400 error_header();
2401 fprintf(stderr, "MariaDB Audit Plugin version %s%s STARTED.\n",
2402 PLUGIN_STR_VERSION, PLUGIN_DEBUG_VERSION);
2403
2404 /* The Query Cache shadows TABLE events if the result is taken from it */
2405 /* so we warn users if both Query Cashe and TABLE events enabled. */
2406 if (!started_mysql && FILTER(EVENT_TABLE))
2407 {
2408 ulonglong *qc_size= (ulonglong *) dlsym(RTLD_DEFAULT, "query_cache_size");
2409 if (qc_size == NULL || *qc_size != 0)
2410 {
2411 struct loc_system_variables *g_sys_var=
2412 (struct loc_system_variables *) dlsym(RTLD_DEFAULT,
2413 "global_system_variables");
2414 if (g_sys_var && g_sys_var->query_cache_type != 0)
2415 {
2416 error_header();
2417 fprintf(stderr, "Query cache is enabled with the TABLE events."
2418 " Some table reads can be veiled.");
2419 }
2420 }
2421 }
2422
2423 ci_disconnect_buffer.header= 10;
2424 ci_disconnect_buffer.thread_id= 0;
2425 ci_disconnect_buffer.query_id= 0;
2426 ci_disconnect_buffer.db_length= 0;
2427 ci_disconnect_buffer.user_length= 0;
2428 ci_disconnect_buffer.host_length= 0;
2429 ci_disconnect_buffer.ip_length= 0;
2430 ci_disconnect_buffer.query= empty_str;
2431 ci_disconnect_buffer.query_length= 0;
2432
2433 if (logging)
2434 start_logging();
2435
2436 init_done= 1;
2437 return 0;
2438}
2439
2440
2441static int server_audit_init_mysql(void *p)
2442{
2443 started_mysql= 1;
2444 mode= 1;
2445 mode_readonly= 1;
2446 return server_audit_init(p);
2447}
2448
2449
2450static int server_audit_deinit(void *p __attribute__((unused)))
2451{
2452 if (!init_done)
2453 return 0;
2454
2455 init_done= 0;
2456 coll_free(&incl_user_coll);
2457 coll_free(&excl_user_coll);
2458
2459 if (output_type == OUTPUT_FILE && logfile)
2460 logger_close(logfile);
2461 else if (output_type == OUTPUT_SYSLOG)
2462 closelog();
2463
2464 (void) free(big_buffer);
2465 flogger_mutex_destroy(&lock_operations);
2466 flogger_mutex_destroy(&lock_bigbuffer);
2467
2468 error_header();
2469 fprintf(stderr, "STOPPED\n");
2470 return 0;
2471}
2472
2473
2474static void rotate_log(MYSQL_THD thd __attribute__((unused)),
2475 struct st_mysql_sys_var *var __attribute__((unused)),
2476 void *var_ptr __attribute__((unused)),
2477 const void *save __attribute__((unused)))
2478{
2479 if (output_type == OUTPUT_FILE && logfile && *(my_bool*) save)
2480 (void) logger_rotate(logfile);
2481}
2482
2483
2484static struct st_mysql_audit mysql_descriptor =
2485{
2486 MYSQL_AUDIT_INTERFACE_VERSION,
2487 NULL,
2488 auditing,
2489 { MYSQL_AUDIT_GENERAL_CLASSMASK | MYSQL_AUDIT_CONNECTION_CLASSMASK }
2490};
2491
2492
2493mysql_declare_plugin(server_audit)
2494{
2495 MYSQL_AUDIT_PLUGIN,
2496 &mysql_descriptor,
2497 "SERVER_AUDIT",
2498 " Alexey Botchkov (MariaDB Corporation)",
2499 "Audit the server activity",
2500 PLUGIN_LICENSE_GPL,
2501 server_audit_init_mysql,
2502 server_audit_deinit,
2503 PLUGIN_VERSION,
2504 audit_status,
2505 vars,
2506 NULL,
2507 0
2508}
2509mysql_declare_plugin_end;
2510
2511
2512static struct st_mysql_audit maria_descriptor =
2513{
2514 MYSQL_AUDIT_INTERFACE_VERSION,
2515 NULL,
2516 auditing,
2517 { MYSQL_AUDIT_GENERAL_CLASSMASK |
2518 MYSQL_AUDIT_TABLE_CLASSMASK |
2519 MYSQL_AUDIT_CONNECTION_CLASSMASK }
2520};
2521maria_declare_plugin(server_audit)
2522{
2523 MYSQL_AUDIT_PLUGIN,
2524 &maria_descriptor,
2525 "SERVER_AUDIT",
2526 "Alexey Botchkov (MariaDB Corporation)",
2527 "Audit the server activity",
2528 PLUGIN_LICENSE_GPL,
2529 server_audit_init,
2530 server_audit_deinit,
2531 PLUGIN_VERSION,
2532 audit_status,
2533 vars,
2534 PLUGIN_STR_VERSION,
2535 MariaDB_PLUGIN_MATURITY_STABLE
2536}
2537maria_declare_plugin_end;
2538
2539
2540static void mark_always_logged(MYSQL_THD thd)
2541{
2542 struct connection_info *cn;
2543 if (thd && (cn= get_loc_info(thd)))
2544 cn->log_always= 1;
2545}
2546
2547
2548static void log_current_query(MYSQL_THD thd)
2549{
2550 struct connection_info *cn;
2551 if (!thd)
2552 return;
2553 cn= get_loc_info(thd);
2554 if (!ci_needs_setup(cn) && cn->query_length &&
2555 FILTER(EVENT_QUERY) && do_log_user(cn->user))
2556 {
2557 log_statement_ex(cn, cn->query_time, thd_get_thread_id(thd),
2558 cn->query, cn->query_length, 0, "QUERY");
2559 cn->log_always= 1;
2560 }
2561}
2562
2563
2564static void update_file_path(MYSQL_THD thd,
2565 struct st_mysql_sys_var *var __attribute__((unused)),
2566 void *var_ptr __attribute__((unused)), const void *save)
2567{
2568 char *new_name= (*(char **) save) ? *(char **) save : empty_str;
2569
2570 if (!maria_55_started || !debug_server_started)
2571 flogger_mutex_lock(&lock_operations);
2572 internal_stop_logging= 1;
2573 error_header();
2574 fprintf(stderr, "Log file name was changed to '%s'.\n", new_name);
2575
2576 if (logging)
2577 log_current_query(thd);
2578
2579 if (logging && output_type == OUTPUT_FILE)
2580 {
2581 char *sav_path= file_path;
2582
2583 file_path= new_name;
2584 internal_stop_logging= 1;
2585 stop_logging();
2586 if (start_logging())
2587 {
2588 file_path= sav_path;
2589 error_header();
2590 fprintf(stderr, "Reverting log filename back to '%s'.\n", file_path);
2591 logging= (start_logging() == 0);
2592 if (!logging)
2593 {
2594 error_header();
2595 fprintf(stderr, "Logging was disabled..\n");
2596 CLIENT_ERROR(1, "Logging was disabled.", MYF(ME_JUST_WARNING));
2597 }
2598 goto exit_func;
2599 }
2600 internal_stop_logging= 0;
2601 }
2602
2603 strncpy(path_buffer, new_name, sizeof(path_buffer));
2604 path_buffer[sizeof(path_buffer)-1]= 0;
2605 file_path= path_buffer;
2606exit_func:
2607 internal_stop_logging= 0;
2608 if (!maria_55_started || !debug_server_started)
2609 flogger_mutex_unlock(&lock_operations);
2610}
2611
2612
2613static void update_file_rotations(MYSQL_THD thd __attribute__((unused)),
2614 struct st_mysql_sys_var *var __attribute__((unused)),
2615 void *var_ptr __attribute__((unused)), const void *save)
2616{
2617 rotations= *(unsigned int *) save;
2618 error_header();
2619 fprintf(stderr, "Log file rotations was changed to '%d'.\n", rotations);
2620
2621 if (!logging || output_type != OUTPUT_FILE)
2622 return;
2623
2624 flogger_mutex_lock(&lock_operations);
2625 logfile->rotations= rotations;
2626 flogger_mutex_unlock(&lock_operations);
2627}
2628
2629
2630static void update_file_rotate_size(MYSQL_THD thd __attribute__((unused)),
2631 struct st_mysql_sys_var *var __attribute__((unused)),
2632 void *var_ptr __attribute__((unused)), const void *save)
2633{
2634 file_rotate_size= *(unsigned long long *) save;
2635 error_header();
2636 fprintf(stderr, "Log file rotate size was changed to '%lld'.\n",
2637 file_rotate_size);
2638
2639 if (!logging || output_type != OUTPUT_FILE)
2640 return;
2641
2642 flogger_mutex_lock(&lock_operations);
2643 logfile->size_limit= file_rotate_size;
2644 flogger_mutex_unlock(&lock_operations);
2645}
2646
2647
2648static void update_incl_users(MYSQL_THD thd,
2649 struct st_mysql_sys_var *var __attribute__((unused)),
2650 void *var_ptr __attribute__((unused)), const void *save)
2651{
2652 char *new_users= (*(char **) save) ? *(char **) save : empty_str;
2653 if (!maria_55_started || !debug_server_started)
2654 flogger_mutex_lock(&lock_operations);
2655 mark_always_logged(thd);
2656 strncpy(incl_user_buffer, new_users, sizeof(incl_user_buffer));
2657 incl_user_buffer[sizeof(incl_user_buffer)-1]= 0;
2658 incl_users= incl_user_buffer;
2659 user_coll_fill(&incl_user_coll, incl_users, &excl_user_coll, 1);
2660 error_header();
2661 fprintf(stderr, "server_audit_incl_users set to '%s'.\n", incl_users);
2662 if (!maria_55_started || !debug_server_started)
2663 flogger_mutex_unlock(&lock_operations);
2664}
2665
2666
2667static void update_excl_users(MYSQL_THD thd __attribute__((unused)),
2668 struct st_mysql_sys_var *var __attribute__((unused)),
2669 void *var_ptr __attribute__((unused)), const void *save)
2670{
2671 char *new_users= (*(char **) save) ? *(char **) save : empty_str;
2672 if (!maria_55_started || !debug_server_started)
2673 flogger_mutex_lock(&lock_operations);
2674 mark_always_logged(thd);
2675 strncpy(excl_user_buffer, new_users, sizeof(excl_user_buffer));
2676 excl_user_buffer[sizeof(excl_user_buffer)-1]= 0;
2677 excl_users= excl_user_buffer;
2678 user_coll_fill(&excl_user_coll, excl_users, &incl_user_coll, 0);
2679 error_header();
2680 fprintf(stderr, "server_audit_excl_users set to '%s'.\n", excl_users);
2681 if (!maria_55_started || !debug_server_started)
2682 flogger_mutex_unlock(&lock_operations);
2683}
2684
2685
2686static void update_output_type(MYSQL_THD thd,
2687 struct st_mysql_sys_var *var __attribute__((unused)),
2688 void *var_ptr __attribute__((unused)), const void *save)
2689{
2690 ulong new_output_type= *((ulong *) save);
2691 if (output_type == new_output_type)
2692 return;
2693
2694 flogger_mutex_lock(&lock_operations);
2695 internal_stop_logging= 1;
2696 if (logging)
2697 {
2698 log_current_query(thd);
2699 stop_logging();
2700 }
2701
2702 output_type= new_output_type;
2703 error_header();
2704 fprintf(stderr, "Output was redirected to '%s'\n",
2705 output_type_names[output_type]);
2706
2707 if (logging)
2708 start_logging();
2709 internal_stop_logging= 0;
2710 flogger_mutex_unlock(&lock_operations);
2711}
2712
2713
2714static void update_syslog_facility(MYSQL_THD thd __attribute__((unused)),
2715 struct st_mysql_sys_var *var __attribute__((unused)),
2716 void *var_ptr __attribute__((unused)), const void *save)
2717{
2718 ulong new_facility= *((ulong *) save);
2719 if (syslog_facility == new_facility)
2720 return;
2721
2722 mark_always_logged(thd);
2723 error_header();
2724 fprintf(stderr, "SysLog facility was changed from '%s' to '%s'.\n",
2725 syslog_facility_names[syslog_facility],
2726 syslog_facility_names[new_facility]);
2727 syslog_facility= new_facility;
2728}
2729
2730
2731static void update_syslog_priority(MYSQL_THD thd __attribute__((unused)),
2732 struct st_mysql_sys_var *var __attribute__((unused)),
2733 void *var_ptr __attribute__((unused)), const void *save)
2734{
2735 ulong new_priority= *((ulong *) save);
2736 if (syslog_priority == new_priority)
2737 return;
2738
2739 flogger_mutex_lock(&lock_operations);
2740 mark_always_logged(thd);
2741 flogger_mutex_unlock(&lock_operations);
2742 error_header();
2743 fprintf(stderr, "SysLog priority was changed from '%s' to '%s'.\n",
2744 syslog_priority_names[syslog_priority],
2745 syslog_priority_names[new_priority]);
2746 syslog_priority= new_priority;
2747}
2748
2749
2750static void update_logging(MYSQL_THD thd,
2751 struct st_mysql_sys_var *var __attribute__((unused)),
2752 void *var_ptr __attribute__((unused)), const void *save)
2753{
2754 char new_logging= *(char *) save;
2755 if (new_logging == logging)
2756 return;
2757
2758 if (!maria_55_started || !debug_server_started)
2759 flogger_mutex_lock(&lock_operations);
2760 internal_stop_logging= 1;
2761 if ((logging= new_logging))
2762 {
2763 start_logging();
2764 if (!logging)
2765 {
2766 CLIENT_ERROR(1, "Logging was disabled.", MYF(ME_JUST_WARNING));
2767 }
2768 }
2769 else
2770 {
2771 log_current_query(thd);
2772 stop_logging();
2773 }
2774
2775 internal_stop_logging= 0;
2776 if (!maria_55_started || !debug_server_started)
2777 flogger_mutex_unlock(&lock_operations);
2778}
2779
2780
2781static void update_mode(MYSQL_THD thd __attribute__((unused)),
2782 struct st_mysql_sys_var *var __attribute__((unused)),
2783 void *var_ptr __attribute__((unused)), const void *save)
2784{
2785 unsigned int new_mode= *(unsigned int *) save;
2786 if (mode_readonly || new_mode == mode)
2787 return;
2788
2789 if (!maria_55_started || !debug_server_started)
2790 flogger_mutex_lock(&lock_operations);
2791 internal_stop_logging= 1;
2792 mark_always_logged(thd);
2793 error_header();
2794 fprintf(stderr, "Logging mode was changed from %d to %d.\n", mode, new_mode);
2795 mode= new_mode;
2796 internal_stop_logging= 0;
2797 if (!maria_55_started || !debug_server_started)
2798 flogger_mutex_unlock(&lock_operations);
2799}
2800
2801
2802static void update_syslog_ident(MYSQL_THD thd __attribute__((unused)),
2803 struct st_mysql_sys_var *var __attribute__((unused)),
2804 void *var_ptr __attribute__((unused)), const void *save)
2805{
2806 char *new_ident= (*(char **) save) ? *(char **) save : empty_str;
2807 strncpy(syslog_ident_buffer, new_ident, sizeof(syslog_ident_buffer));
2808 syslog_ident_buffer[sizeof(syslog_ident_buffer)-1]= 0;
2809 syslog_ident= syslog_ident_buffer;
2810 error_header();
2811 fprintf(stderr, "SYSYLOG ident was changed to '%s'\n", syslog_ident);
2812 flogger_mutex_lock(&lock_operations);
2813 mark_always_logged(thd);
2814 if (logging && output_type == OUTPUT_SYSLOG)
2815 {
2816 stop_logging();
2817 start_logging();
2818 }
2819 flogger_mutex_unlock(&lock_operations);
2820}
2821
2822
2823struct st_my_thread_var *loc_thread_var(void)
2824{
2825 return 0;
2826}
2827
2828
2829
2830#ifdef _WIN32
2831BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
2832{
2833 if (fdwReason != DLL_PROCESS_ATTACH)
2834 return 1;
2835
2836 serv_ver= (const char *) GetProcAddress(0, "server_version");
2837#else
2838void __attribute__ ((constructor)) audit_plugin_so_init(void)
2839{
2840 serv_ver= server_version;
2841#endif /*_WIN32*/
2842
2843 if (!serv_ver)
2844 goto exit;
2845
2846 started_mariadb= strstr(serv_ver, "MariaDB") != 0;
2847 debug_server_started= strstr(serv_ver, "debug") != 0;
2848
2849 if (started_mariadb)
2850 {
2851 if (serv_ver[0] == '1')
2852 use_event_data_for_disconnect= 1;
2853 else
2854 maria_55_started= 1;
2855 }
2856 else
2857 {
2858 /* Started MySQL. */
2859 if (serv_ver[0] == '5' && serv_ver[2] == '5')
2860 {
2861 int sc= serv_ver[4] - '0';
2862 if (serv_ver[5] >= '0' && serv_ver[5] <= '9')
2863 sc= sc * 10 + serv_ver[5] - '0';
2864 if (sc <= 10)
2865 {
2866 mysql_descriptor.interface_version= 0x0200;
2867 mysql_descriptor.event_notify= (void *) auditing_v8;
2868 }
2869 else if (sc < 14)
2870 {
2871 mysql_descriptor.interface_version= 0x0200;
2872 mysql_descriptor.event_notify= (void *) auditing_v13;
2873 }
2874 }
2875 else if (serv_ver[0] == '5' && serv_ver[2] == '6')
2876 {
2877 int sc= serv_ver[4] - '0';
2878 if (serv_ver[5] >= '0' && serv_ver[5] <= '9')
2879 sc= sc * 10 + serv_ver[5] - '0';
2880 if (sc >= 24)
2881 use_event_data_for_disconnect= 1;
2882 }
2883 else if ((serv_ver[0] == '5' && serv_ver[2] == '7') ||
2884 (serv_ver[0] == '8' && serv_ver[2] == '0'))
2885 {
2886 mysql_57_started= 1;
2887 _mysql_plugin_declarations_[0].info= mysql_v4_descriptor;
2888 use_event_data_for_disconnect= 1;
2889 }
2890 MYSQL_SYSVAR_NAME(loc_info).flags= PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL |
2891 PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC;
2892 }
2893
2894 memset(locinfo_ini_value, 'O', sizeof(locinfo_ini_value)-1);
2895 locinfo_ini_value[sizeof(locinfo_ini_value)-1]= 0;
2896
2897exit:
2898#ifdef _WIN32
2899 return 1;
2900#else
2901 return;
2902#endif
2903}
2904
2905