| 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 | |