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) |
29 | static 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 | |
110 | static char **int_mysql_data_home; |
111 | static 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 |
125 | static 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 | |
138 | static 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 | |
150 | static 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 | |
185 | static 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 | |
201 | static 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 | |
224 | static 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 | |
272 | extern char server_version[]; |
273 | static const char *serv_ver= NULL; |
274 | static int started_mysql= 0; |
275 | static int mysql_57_started= 0; |
276 | static int debug_server_started= 0; |
277 | static int use_event_data_for_disconnect= 0; |
278 | static int started_mariadb= 0; |
279 | static int maria_55_started= 0; |
280 | static int maria_above_5= 0; |
281 | static char *incl_users, *excl_users, |
282 | *file_path, *syslog_info; |
283 | static char path_buffer[FN_REFLEN]; |
284 | static unsigned int mode, mode_readonly= 0; |
285 | static ulong output_type; |
286 | static ulong syslog_facility, syslog_priority; |
287 | |
288 | static ulonglong events; /* mask for events to log */ |
289 | static unsigned long long file_rotate_size; |
290 | static unsigned int rotations; |
291 | static my_bool rotate= TRUE; |
292 | static char logging; |
293 | static int internal_stop_logging= 0; |
294 | static char incl_user_buffer[1024]; |
295 | static char excl_user_buffer[1024]; |
296 | static char *big_buffer= NULL; |
297 | static size_t big_buffer_alloced= 0; |
298 | static unsigned int query_log_limit= 0; |
299 | |
300 | static char servhost[256]; |
301 | static uint servhost_len; |
302 | static char *syslog_ident; |
303 | static char syslog_ident_buffer[128]= "mysql-server_auditing" ; |
304 | |
305 | struct connection_info |
306 | { |
307 | int ; |
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 |
326 | static char default_file_name[DEFAULT_FILENAME_LEN+1]= "server_audit.log" ; |
327 | |
328 | static void update_file_path(MYSQL_THD thd, struct st_mysql_sys_var *var, |
329 | void *var_ptr, const void *save); |
330 | static void update_file_rotate_size(MYSQL_THD thd, struct st_mysql_sys_var *var, |
331 | void *var_ptr, const void *save); |
332 | static void update_file_rotations(MYSQL_THD thd, struct st_mysql_sys_var *var, |
333 | void *var_ptr, const void *save); |
334 | static void update_incl_users(MYSQL_THD thd, struct st_mysql_sys_var *var, |
335 | void *var_ptr, const void *save); |
336 | static void update_excl_users(MYSQL_THD thd, struct st_mysql_sys_var *var, |
337 | void *var_ptr, const void *save); |
338 | static void update_output_type(MYSQL_THD thd, struct st_mysql_sys_var *var, |
339 | void *var_ptr, const void *save); |
340 | static void update_syslog_facility(MYSQL_THD thd, struct st_mysql_sys_var *var, |
341 | void *var_ptr, const void *save); |
342 | static void update_syslog_priority(MYSQL_THD thd, struct st_mysql_sys_var *var, |
343 | void *var_ptr, const void *save); |
344 | static void update_mode(MYSQL_THD thd, struct st_mysql_sys_var *var, |
345 | void *var_ptr, const void *save); |
346 | static void update_logging(MYSQL_THD thd, struct st_mysql_sys_var *var, |
347 | void *var_ptr, const void *save); |
348 | static void update_syslog_ident(MYSQL_THD thd, struct st_mysql_sys_var *var, |
349 | void *var_ptr, const void *save); |
350 | static void rotate_log(MYSQL_THD thd, struct st_mysql_sys_var *var, |
351 | void *var_ptr, const void *save); |
352 | |
353 | static MYSQL_SYSVAR_STR(incl_users, incl_users, PLUGIN_VAR_RQCMDARG, |
354 | "Comma separated list of users to monitor." , |
355 | NULL, update_incl_users, NULL); |
356 | static 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 | |
369 | static const char *event_names[]= |
370 | { |
371 | "CONNECT" , "QUERY" , "TABLE" , "QUERY_DDL" , "QUERY_DML" , "QUERY_DCL" , |
372 | "QUERY_DML_NO_SELECT" , NULL |
373 | }; |
374 | static TYPELIB events_typelib= |
375 | { |
376 | array_elements(event_names) - 1, "" , event_names, NULL |
377 | }; |
378 | static 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 |
385 | static const char *output_type_names[]= { "syslog" , "file" , 0 }; |
386 | static TYPELIB output_typelib= |
387 | { |
388 | array_elements(output_type_names) - 1, "output_typelib" , |
389 | output_type_names, NULL |
390 | }; |
391 | static 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); |
395 | static MYSQL_SYSVAR_STR(file_path, file_path, PLUGIN_VAR_RQCMDARG, |
396 | "Path to the log file." , NULL, update_file_path, default_file_name); |
397 | static 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); |
401 | static 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); |
404 | static MYSQL_SYSVAR_BOOL(file_rotate_now, rotate, PLUGIN_VAR_OPCMDARG, |
405 | "Force log rotation now." , NULL, rotate_log, FALSE); |
406 | static MYSQL_SYSVAR_BOOL(logging, logging, |
407 | PLUGIN_VAR_OPCMDARG, "Turn on/off the logging." , NULL, |
408 | update_logging, 0); |
409 | static MYSQL_SYSVAR_UINT(mode, mode, |
410 | PLUGIN_VAR_OPCMDARG, "Auditing mode." , NULL, update_mode, 0, 0, 1, 1); |
411 | static 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); |
414 | static 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, "" ); |
417 | static 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 | |
421 | char locinfo_ini_value[sizeof(struct connection_info)+4]; |
422 | |
423 | static MYSQL_THDVAR_STR(loc_info, |
424 | PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_MEMALLOC, |
425 | "Internal info" , NULL, NULL, locinfo_ini_value); |
426 | |
427 | static 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 |
443 | static 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 |
458 | static TYPELIB syslog_facility_typelib= |
459 | { |
460 | array_elements(syslog_facility_names) - 1, "syslog_facility_typelib" , |
461 | syslog_facility_names, NULL |
462 | }; |
463 | static 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 | |
468 | static 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 |
476 | static 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 | |
483 | static TYPELIB syslog_priority_typelib= |
484 | { |
485 | array_elements(syslog_priority_names) - 1, "syslog_priority_typelib" , |
486 | syslog_priority_names, NULL |
487 | }; |
488 | static 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 | |
494 | static 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 */ |
516 | static int is_active= 0; |
517 | static long log_write_failures= 0; |
518 | static char current_log_buf[FN_REFLEN]= "" ; |
519 | static char last_error_buf[512]= "" ; |
520 | |
521 | extern void *mysql_v4_descriptor; |
522 | |
523 | static 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 */ |
534 | static PSI_mutex_key key_LOCK_operations; |
535 | static PSI_mutex_key key_LOCK_bigbuffer; |
536 | static 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 |
544 | static mysql_mutex_t lock_operations; |
545 | static 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 | |
555 | static 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 | |
566 | static void blank_user(char *user) |
567 | { |
568 | for (; *user && *user != ','; user++) |
569 | *user= ' '; |
570 | } |
571 | |
572 | |
573 | static 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 | |
595 | static 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 | |
626 | struct user_name |
627 | { |
628 | size_t name_len; |
629 | char *name; |
630 | }; |
631 | |
632 | |
633 | struct user_coll |
634 | { |
635 | int n_users; |
636 | struct user_name *users; |
637 | int n_alloced; |
638 | }; |
639 | |
640 | |
641 | static 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 | |
649 | static 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 | |
659 | static 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 | |
671 | static 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 | |
683 | static 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 | |
703 | static void coll_sort(struct user_coll *c) |
704 | { |
705 | qsort(c->users, c->n_users, sizeof(c->users[0]), cmp_users); |
706 | } |
707 | |
708 | |
709 | static 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 | |
775 | enum 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 | |
791 | struct sa_keyword |
792 | { |
793 | int length; |
794 | const char *wd; |
795 | struct sa_keyword *next; |
796 | enum sa_keywords type; |
797 | }; |
798 | |
799 | |
800 | struct sa_keyword xml_word= {3, "XML" , 0, SQLCOM_NOTHING}; |
801 | struct sa_keyword user_word= {4, "USER" , 0, SQLCOM_NOTHING}; |
802 | struct sa_keyword data_word= {4, "DATA" , 0, SQLCOM_NOTHING}; |
803 | struct sa_keyword server_word= {6, "SERVER" , 0, SQLCOM_NOTHING}; |
804 | struct sa_keyword master_word= {6, "MASTER" , 0, SQLCOM_NOTHING}; |
805 | struct sa_keyword password_word= {8, "PASSWORD" , 0, SQLCOM_NOTHING}; |
806 | struct sa_keyword function_word= {8, "FUNCTION" , 0, SQLCOM_NOTHING}; |
807 | struct sa_keyword statement_word= {9, "STATEMENT" , 0, SQLCOM_NOTHING}; |
808 | struct sa_keyword procedure_word= {9, "PROCEDURE" , 0, SQLCOM_NOTHING}; |
809 | |
810 | |
811 | struct sa_keyword keywords_to_skip[]= |
812 | { |
813 | {3, "SET" , &statement_word, SQLCOM_QUERY_ADMIN}, |
814 | {0, NULL, 0, SQLCOM_DDL} |
815 | }; |
816 | |
817 | |
818 | struct 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 | |
831 | struct 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 | |
842 | struct 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 | |
858 | struct 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 | |
873 | struct 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 | |
885 | struct 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 | |
899 | static void () |
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 | |
913 | static LOGGER_HANDLE *logfile; |
914 | static struct user_coll incl_user_coll, excl_user_coll; |
915 | static unsigned long long query_counter= 1; |
916 | |
917 | |
918 | static struct connection_info *get_loc_info(MYSQL_THD thd) |
919 | { |
920 | return (struct connection_info *) THDVAR(thd, loc_info); |
921 | } |
922 | |
923 | |
924 | static int ci_needs_setup(const struct connection_info *ci) |
925 | { |
926 | return ci->header != 0; |
927 | } |
928 | |
929 | |
930 | static 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 | |
942 | static 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 | |
1003 | static 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 | |
1073 | static 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 | |
1092 | static 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 | |
1103 | static 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) |
1123 | static char empty_str[1]= { 0 }; |
1124 | |
1125 | |
1126 | static int is_space(char c) |
1127 | { |
1128 | return c == ' ' || c == '\r' || c == '\n' || c == '\t'; |
1129 | } |
1130 | |
1131 | |
1132 | #define SKIP_SPACES(str) \ |
1133 | do { \ |
1134 | while (is_space(*str)) \ |
1135 | ++str; \ |
1136 | } while(0) |
1137 | |
1138 | |
1139 | #define ESC_MAP_SIZE 0x60 |
1140 | static 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 | |
1150 | static char escaped_char(char c) |
1151 | { |
1152 | return ((unsigned char ) c) >= ESC_MAP_SIZE ? 0 : esc_map[(unsigned char) c]; |
1153 | } |
1154 | |
1155 | |
1156 | static 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 | |
1191 | static 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 | |
1210 | static 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 | |
1244 | static 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 | |
1253 | static 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 | |
1273 | static size_t (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 | |
1310 | static 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 | |
1332 | static 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 | |
1353 | static 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 | |
1383 | static 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 | } |
1455 | no_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 | |
1478 | static 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 | |
1496 | static 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 | |
1517 | static 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 | }; |
1577 | do_loop: |
1578 | l_keywords++; |
1579 | } |
1580 | |
1581 | not_in_list: |
1582 | return qwe_in_list; |
1583 | } |
1584 | |
1585 | |
1586 | static 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; |
1666 | do_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 | |
1745 | static 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 | |
1755 | static 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 | |
1777 | static 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 | |
1801 | static 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 | |
1812 | static 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 | |
1833 | static struct connection_info ci_disconnect_buffer; |
1834 | |
1835 | #define AA_FREE_CONNECTION 1 |
1836 | #define AA_CHANGE_USER 2 |
1837 | |
1838 | static 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 | |
1974 | struct connection_info cn_error_buffer; |
1975 | |
1976 | |
1977 | #define FILTER(MASK) (events == 0 || (events & MASK)) |
1978 | void 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 | } |
2090 | exit_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 | |
2114 | struct 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 | |
2132 | static 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 | |
2201 | static 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 | |
2215 | int 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 | */ |
2255 | typedef 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 | |
2326 | static int init_done= 0; |
2327 | |
2328 | static 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 | |
2441 | static 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 | |
2450 | static 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 | |
2474 | static 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 | |
2484 | static 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 | |
2493 | mysql_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 | } |
2509 | mysql_declare_plugin_end; |
2510 | |
2511 | |
2512 | static 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 | }; |
2521 | maria_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 | } |
2537 | maria_declare_plugin_end; |
2538 | |
2539 | |
2540 | static 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 | |
2548 | static 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 | |
2564 | static 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; |
2606 | exit_func: |
2607 | internal_stop_logging= 0; |
2608 | if (!maria_55_started || !debug_server_started) |
2609 | flogger_mutex_unlock(&lock_operations); |
2610 | } |
2611 | |
2612 | |
2613 | static 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 | |
2630 | static 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 | |
2648 | static 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 | |
2667 | static 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 | |
2686 | static 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 | |
2714 | static 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 | |
2731 | static 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 | |
2750 | static 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 | |
2781 | static 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 | |
2802 | static 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 | |
2823 | struct st_my_thread_var *loc_thread_var(void) |
2824 | { |
2825 | return 0; |
2826 | } |
2827 | |
2828 | |
2829 | |
2830 | #ifdef _WIN32 |
2831 | BOOL 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 |
2838 | void __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 | |
2897 | exit: |
2898 | #ifdef _WIN32 |
2899 | return 1; |
2900 | #else |
2901 | return; |
2902 | #endif |
2903 | } |
2904 | |
2905 | |