diff --git a/include/audit_handler.h b/include/audit_handler.h index 5c224fb..02df63b 100644 --- a/include/audit_handler.h +++ b/include/audit_handler.h @@ -17,7 +17,7 @@ #include -#define AUDIT_LOG_PREFIX "Audit Plugin:" +#define AUDIT_LOG_PREFIX "McAfee Audit Plugin:" #define AUDIT_PROTOCOL_VERSION "1.0" #if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709 @@ -218,6 +218,8 @@ public: static QueryTableInf *getQueryCacheTableList1(THD *thd); // utility functions for fetching thd stuff + static int thd_client_port(THD *thd); + static inline my_thread_id thd_inst_thread_id(THD *thd) { return *(my_thread_id *) (((unsigned char *) thd) @@ -475,29 +477,6 @@ static inline const CHARSET_INFO * pfs_connect_attrs_cs(void * pfs) return sock; } - static inline int thd_client_port(THD *thd) - { - if (! Audit_formatter::thd_offsets.net) - { - return -1; - } - NET *net = ((NET *) (((unsigned char *) thd) - + Audit_formatter::thd_offsets.net)); - - // get the port for the remote end - int port = -1; - - if (net->vio != NULL) // MySQL 5.7.17 - this can happen. :-( - { - struct sockaddr_in *in; - - in = (struct sockaddr_in *) & net->vio->remote; - port = in->sin_port; - } - - return port; - } - static inline ulonglong thd_found_rows(THD *thd) { if (Audit_formatter::thd_offsets.found_rows == 0) @@ -899,7 +878,8 @@ class Audit_socket_handler: public Audit_io_handler { public: Audit_socket_handler() : - m_vio(NULL), m_connect_timeout(1), m_write_timeout(0) + m_vio(NULL), m_connect_timeout(1), m_write_timeout(0), + m_log_with_error_severity(false) { m_io_type = "socket"; } @@ -932,6 +912,9 @@ protected: // Vio we write to // define as void* so we don't access members directly void *m_vio; + + // log using error severity only the second time same issue occurs + bool m_log_with_error_severity; }; #endif /* AUDIT_HANDLER_H_ */ diff --git a/offset-extract/offset-extract.sh b/offset-extract/offset-extract.sh index bf9f023..8ab1560 100644 --- a/offset-extract/offset-extract.sh +++ b/offset-extract/offset-extract.sh @@ -59,7 +59,7 @@ fi # In 5.6.15 and up, 5.7 and mariabdb 10.0.11 and up, mariadb 10.1 # m_session_connect_attrs_cs changed to m_session_connect_attrs_cs_number -if echo $MYVER | grep -P '^(5\.7|10\.1|5\.6\.(1[5-9]|[2-9][0-9])|10.0.(1[1-9]|[2-9][0-9]))' > /dev/null +if echo $MYVER | grep -P '^(5\.7|10\.[1-2]|5\.6\.(1[5-9]|[2-9][0-9])|10.0.(1[1-9]|[2-9][0-9]))' > /dev/null then CONNECT_ATTRS_CS=m_session_connect_attrs_cs_number fi diff --git a/plugin-name.txt b/plugin-name.txt new file mode 100644 index 0000000..9383b80 --- /dev/null +++ b/plugin-name.txt @@ -0,0 +1,54 @@ +Tue, Jun 27, 2017 10:48:38 AM +============================= + +By default, the McAfee AUDIT plugin for MySQL* is named "AUDIT" and +that is the name you should use when installing the plugin with the SQL +"INSTALL PLUGIN" command. + +It is the "AUDIT" name that provides the "audit_" prefix to the plugin's +various configuration variables. + +In order to avoid conflict with other vendors' auditing plugins whose +names may start with "audit" (such as MySQL's "audit_log" plugin) it +is possible to change the name of the McAfee plugin. The steps are +as follows: + +1. If you're currently using the McAfee plugin, unload it. + +2. Edit the /usr/bin/mysqld_safe shell script (using the correct location +for your system). For MySQL 5.7.9, look for the eval_log_error() function. +Before the line that says: + + eval "$cmd" + +add a line like this: + + export MCAFEE_AUDIT_PLUGIN_NAME=MCAFEE # use any name you want + +You can use any name you like, "MCAFEE" is just an example. + +For other MySQL versions, determine where the mysqld daemon is actually +started, and set the environment variable right before that. + +3. After restarting MySQL, you will need to load the plugin using the +new name. From the MySQL client: + + install plugin MCAFEE soname 'libaudit_plugin.so'; + +and/or from /etc/my.cnf: + + [mysqld] + plugin-load=MCAFEE=libaudit_plugin.so + +Once you've done that, you must remember that the names of ALL the +configuration variables will start with the lowercase version of the +name you've chosen. For example, "mcafee_json_log_file" instead of +"audit_json_log_file". + +If you previously had various "audit_XXX" variables set in your +/etc/my.cnf file, you will need to rename them! Otherwise MySQL will +fail to start, with an error about unknown variables. + +That's it! Good luck. + +* Other trademarks and brands may be claimed as the property of others. diff --git a/src/audit_handler.cc b/src/audit_handler.cc index de63dd7..f0583a4 100644 --- a/src/audit_handler.cc +++ b/src/audit_handler.cc @@ -116,6 +116,49 @@ const char *Audit_formatter::retrieve_object_type(TABLE_LIST *pObj) return "TABLE"; } +// This routine used to pull the client port out of the thd->net->vio->remote +// object, but on MySQL 5.7 the port is zero. So we resort to getting the +// underlying fd and using getpeername(2) on it. + +int Audit_formatter::thd_client_port(THD *thd) +{ + int port = -1; + int sock = thd_client_fd(thd); + + if (sock < 0) + { + return port; // shouldn't happen + } + + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + + // get port of the guy on the other end of our connection + if (getpeername(sock, (struct sockaddr *) & addr, & len) < 0) + { + return port; + } + + + if (addr.ss_family == AF_INET) + { + struct sockaddr_in *sin = (struct sockaddr_in *) & addr; + port = ntohs(sin->sin_port); + } + else + { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *) & addr; + port = ntohs(sin->sin6_port); + } + + if (port == 0) // shouldn't happen + { + port = -1; + } + + return port; +} + void Audit_handler::stop_all() { for (size_t i = 0; i < MAX_AUDIT_HANDLERS_NUM; ++i) @@ -470,15 +513,32 @@ int Audit_socket_handler::open(const char *io_dest, bool log_errors) #endif { if (log_errors) + { + sql_print_warning( + "%s unable to connect to socket: %s. err: %s.", + AUDIT_LOG_PREFIX, m_io_dest, strerror(errno)); + + // The next time this occurs, log as an error + m_log_with_error_severity = true; + } + // Only if issue persist also in second retry, report it by using 'error' severity. + else if (m_log_with_error_severity) { sql_print_error( "%s unable to connect to socket: %s. err: %s.", AUDIT_LOG_PREFIX, m_io_dest, strerror(errno)); + + m_log_with_error_severity = false; } + close(); return -2; } + // At this point, connected successfully. + // Ensure same behavior in case first time failed but second retry was successful + m_log_with_error_severity = false; + if (m_write_timeout > 0) { int timeout = m_write_timeout / 1000; // milliseconds to seconds, integer dvision diff --git a/src/audit_offsets.cc b/src/audit_offsets.cc index 02a6d4b..0e8f0ff 100644 --- a/src/audit_offsets.cc +++ b/src/audit_offsets.cc @@ -858,6 +858,22 @@ const ThdOffsets thd_offsets_arr[] = const ThdOffsets thd_offsets_arr[] = { /* +++ MARIADB 64 OFFSETS GO HERE +++ */ + //offsets for: /mariadb/10.1.25/bin/mysqld (10.1.25-MariaDB) + {"10.1.25-MariaDB","4b9b78ae51081d0ebc0a74f18a9e0f75", 13648, 13712, 6424, 8032, 88, 3000, 8, 0, 16, 24, 152, 13804, 7800, 3136, 3144, 3148, 568, 0, 0, 13024, 13048, 13032}, + //offsets for: /mariadb/10.2.0/bin/mysqld (10.2.0-MariaDB) + {"10.2.0-MariaDB","f41691936c84306b0028acbc5f898a85", 13648, 13712, 6424, 8032, 88, 3072, 8, 0, 16, 24, 152, 13804, 7800, 3136, 3144, 3148, 568, 0, 0, 13024, 13048, 13032}, + //offsets for: /mariadb/10.2.1/bin/mysqld (10.2.1-MariaDB) + {"10.2.1-MariaDB","5733c708d9eced8125ffb7df3ff35ab4", 13648, 13712, 6424, 8032, 88, 3080, 8, 0, 16, 24, 152, 13812, 7800, 3136, 3144, 3148, 568, 0, 0, 13024, 13048, 13032}, + //offsets for: /mariadb/10.2.2/bin/mysqld (10.2.2-MariaDB) + {"10.2.2-MariaDB","d34d768480e3354431b2889e6e327bd6", 13752, 13816, 6528, 8136, 88, 3152, 8, 0, 16, 24, 152, 13916, 7904, 3136, 3144, 3148, 568, 0, 0, 13128, 13152, 13136}, + //offsets for: /mariadb/10.2.3/bin/mysqld (10.2.3-MariaDB) + {"10.2.3-MariaDB","55d62a7984de41e8473638d3fb028f6d", 13776, 13840, 6544, 8152, 88, 3120, 8, 0, 16, 24, 152, 13940, 7920, 3136, 3144, 3148, 568, 0, 0, 13152, 13176, 13160}, + //offsets for: /mariadb/10.2.4/bin/mysqld (10.2.4-MariaDB) + {"10.2.4-MariaDB","185570d073f337b9efa5fdb5b4c48b3d", 13792, 13856, 6560, 8168, 88, 3120, 8, 0, 16, 24, 152, 13956, 7936, 3136, 3144, 3148, 576, 0, 0, 13168, 13192, 13176}, + //offsets for: /mariadb/10.2.5/bin/mysqld (10.2.5-MariaDB) + {"10.2.5-MariaDB","d80fabd4b7432b28ac19491c28b7afe0", 13792, 13856, 6560, 8168, 88, 3120, 8, 0, 16, 24, 152, 13956, 7936, 3136, 3144, 3148, 576, 0, 0, 13168, 13192, 13176}, + //offsets for: /mariadb/10.2.6/bin/mysqld (10.2.6-MariaDB) + {"10.2.6-MariaDB","fbded44a97a2141b8a054209b9b2588a", 13792, 13856, 6560, 8168, 88, 3128, 8, 0, 16, 24, 152, 13956, 7936, 3136, 3144, 3148, 576, 0, 0, 13168, 13192, 13176}, //offsets for: /mariadb/10.1.24/bin/mysqld (10.1.24-MariaDB) {"10.1.24-MariaDB","2be795c560d6aca4e9d966523176a588", 13648, 13712, 6424, 8032, 88, 3000, 8, 0, 16, 24, 152, 13804, 7800, 3136, 3144, 3148, 568, 0, 0, 13024, 13048, 13032}, //offsets for: /mariadb/10.0.31/bin/mysqld (10.0.31-MariaDB) @@ -1031,6 +1047,22 @@ const ThdOffsets thd_offsets_arr[] = const ThdOffsets thd_offsets_arr[] = { /* +++ MARIADB 32 OFFSETS GO HERE +++ */ + //offsets for: /mariadb/10.1.25/bin/mysqld (10.1.25-MariaDB) + {"10.1.25-MariaDB","12b0ba38e1ded5daf3ac94eab792ae90", 8504, 8540, 3848, 5308, 44, 1908, 4, 0, 8, 12, 84, 8616, 5176, 2604, 2608, 2612, 356, 0, 0, 8028, 8052, 8036}, + //offsets for: /mariadb/10.2.0/bin/mysqld (10.2.0-MariaDB) + {"10.2.0-MariaDB","48cf1c6fc41578d1de4875469748b36a", 8512, 8548, 3852, 5316, 44, 1944, 4, 0, 8, 12, 84, 8628, 5180, 2604, 2608, 2612, 356, 0, 0, 8036, 8060, 8044}, + //offsets for: /mariadb/10.2.1/bin/mysqld (10.2.1-MariaDB) + {"10.2.1-MariaDB","03919f36347c9a784a8a346c019fb462", 8512, 8548, 3852, 5316, 44, 1948, 4, 0, 8, 12, 84, 8636, 5180, 2604, 2608, 2612, 356, 0, 0, 8036, 8060, 8044}, + //offsets for: /mariadb/10.2.2/bin/mysqld (10.2.2-MariaDB) + {"10.2.2-MariaDB","4ff0d974abf58d6f9c315c03adc9f6e1", 8564, 8600, 3904, 5368, 44, 1996, 4, 0, 8, 12, 84, 8688, 5232, 2604, 2608, 2612, 356, 0, 0, 8088, 8112, 8096}, + //offsets for: /mariadb/10.2.3/bin/mysqld (10.2.3-MariaDB) + {"10.2.3-MariaDB","f1a620e35280a2065862216a4d37604d", 8576, 8612, 3912, 5376, 44, 1980, 4, 0, 8, 12, 84, 8700, 5240, 2604, 2608, 2612, 356, 0, 0, 8100, 8124, 8108}, + //offsets for: /mariadb/10.2.4/bin/mysqld (10.2.4-MariaDB) + {"10.2.4-MariaDB","400cd2fc15db447615f59c7f03662335", 8588, 8624, 3924, 5388, 44, 1980, 4, 0, 8, 12, 84, 8712, 5252, 2604, 2608, 2612, 360, 0, 0, 8112, 8136, 8120}, + //offsets for: /mariadb/10.2.5/bin/mysqld (10.2.5-MariaDB) + {"10.2.5-MariaDB","8fb7c12d6792c047ae5baf2629665b07", 8588, 8624, 3924, 5388, 44, 1980, 4, 0, 8, 12, 84, 8712, 5252, 2604, 2608, 2612, 360, 0, 0, 8112, 8136, 8120}, + //offsets for: /mariadb/10.2.6/bin/mysqld (10.2.6-MariaDB) + {"10.2.6-MariaDB","2f2029a9a9a9654cf25174fcfe86e609", 8588, 8624, 3924, 5388, 44, 1984, 4, 0, 8, 12, 84, 8712, 5252, 2604, 2608, 2612, 360, 0, 0, 8112, 8136, 8120}, //offsets for: /mariadb/10.1.24/bin/mysqld (10.1.24-MariaDB) {"10.1.24-MariaDB","16198adda60a75e53b9b9fc5b312a827", 8504, 8540, 3848, 5308, 44, 1908, 4, 0, 8, 12, 84, 8616, 5176, 2604, 2608, 2612, 356, 0, 0, 8028, 8052, 8036}, //offsets for: /mariadb/10.0.31/bin/mysqld (10.0.31-MariaDB) diff --git a/src/audit_plugin.cc b/src/audit_plugin.cc index 2fe4019..f3cc593 100644 --- a/src/audit_plugin.cc +++ b/src/audit_plugin.cc @@ -26,6 +26,7 @@ #include "md5.h" #endif +static const char *PLUGIN_NAME = "AUDIT"; /* @@ -263,7 +264,7 @@ static void initializePeerCredentials(THD *pThd) goto done; } - // Is it a Unix Domain socket? + // Is it a socket? if (fstat(sock, &sbuf) < 0) { goto done; @@ -274,17 +275,9 @@ static void initializePeerCredentials(THD *pThd) goto done; } - // At this point, we know we have a Unix domain socket. - if (! json_formatter.m_write_socket_creds) - { - // We need to set this, so that we don't send the - // client port, but we don't bother getting the command - // name and user name, since they won't be sent. - THDVAR(pThd, peer_is_uds) = TRUE; - goto done; - } - // Do a SO_PEERCRED + // Note, this seems to work on any socket, but won't bring valid + // info except for a Unix Domain Socket. Sigh. if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) { goto done; @@ -295,6 +288,21 @@ static void initializePeerCredentials(THD *pThd) goto done; } + // At this point, we know we have a socket but we don't know what type. + if (! json_formatter.m_write_socket_creds) + { + // We need to set this, so that we don't send the + // client port, but we don't bother getting the command + // name and user name, since they won't be sent. + THDVAR(pThd, peer_is_uds) = (cred.pid != 0); + goto done; + } + + if (cred.pid == 0) + { + goto done; + } + if (peer == NULL) { goto done; @@ -308,7 +316,14 @@ static void initializePeerCredentials(THD *pThd) if (pwbuf == NULL) { // no name, send UID - snprintf(buf, sizeof(buf) - 1, "%lu", (unsigned long) cred.uid); + if (cred.uid >= 0) + { + snprintf(buf, sizeof(buf) - 1, "%lu", (unsigned long) cred.uid); + } + else + { + strcpy(buf, "[unknown]"); + } username = buf; } else @@ -922,7 +937,7 @@ int is_remove_patches(ThdSesData *pThdData) Sql_cmd_uninstall_plugin *up = Audit_formatter::lex_sql_cmd(pLex); LEX_STRING Lex_comment = *(LEX_STRING*)(((unsigned char *) up) + Audit_formatter::thd_offsets.uninstall_cmd_comment); #endif - if (strncasecmp(Lex_comment.str, "AUDIT", 5) == 0) + if (strncasecmp(Lex_comment.str, PLUGIN_NAME, strlen(PLUGIN_NAME)) == 0) { if (! uninstall_plugin_enable) { @@ -2495,7 +2510,7 @@ mysql_declare_plugin(audit_plugin) { plugin_type, &audit_plugin, - "AUDIT", + PLUGIN_NAME, "McAfee Inc", "AUDIT plugin, creates a file mysql-audit.log to log activity", PLUGIN_LICENSE_GPL, @@ -2508,6 +2523,25 @@ mysql_declare_plugin(audit_plugin) } mysql_declare_plugin_end; +static inline void init_peer_info() +{ + memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1); + peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0'; +} + +static inline void set_plugin_name_from_env() +{ + char *plugin_name = getenv("MCAFEE_AUDIT_PLUGIN_NAME"); + + if (plugin_name != NULL) + { + PLUGIN_NAME = plugin_name; + _mysql_plugin_declarations_[0].name = plugin_name; + sql_print_information( + "%s using plugin name %s from environtment", log_prefix, plugin_name); + } +} + #if MYSQL_VERSION_ID < 50505 /** * DLL constructor method. @@ -2530,8 +2564,8 @@ extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void) log_prefix, audit_plugin.interface_version >> 8); } - memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1); - peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0'; + init_peer_info(); + set_plugin_name_from_env(); } #elif MYSQL_VERSION_ID < 50600 extern struct st_mysql_plugin *mysql_mandatory_plugins[]; @@ -2542,8 +2576,8 @@ extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void) log_prefix, audit_plugin.interface_version, audit_plugin.interface_version >> 8); - memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1); - peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0'; + init_peer_info(); + set_plugin_name_from_env(); } #elif !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID < 50709 // Interface version for MySQL 5.6 changed in 5.6.14. @@ -2560,15 +2594,15 @@ extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void) audit_plugin.interface_version = 0x0301; } - memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1); - peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0'; + init_peer_info(); + set_plugin_name_from_env(); } #else // 5.7 extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void) { - memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1); - peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0'; + init_peer_info(); + set_plugin_name_from_env(); } #endif