new vars: offsets_by_version and validate_offsets_extended. By default we try to resolve offsets by version. Also, added extended validation of the offsets.

pull/18/merge
unknown 2012-11-04 13:49:59 +02:00
parent 887f096883
commit 3ad6a1775c
3 changed files with 194 additions and 141 deletions

View File

@ -144,24 +144,6 @@ public:
{
return *(LEX**) (((unsigned char *) thd) + Audit_formatter::thd_offsets.lex);
}
//will return a pointer to the query and set len with the length of the query
static inline const char * thd_query(THD * thd, size_t * len)
{
#if MYSQL_VERSION_ID >= 50505
MYSQL_LEX_STRING * str = thd_query_string(thd);
if(str)
{
*len = str->length;
return str->str;
}
*len = 0;
return NULL;
#else
*len = thd->query_length();
return thd->query();
#endif
}
};

64
src/audit_handler.cc Normal file → Executable file
View File

@ -343,25 +343,49 @@ static inline void yajl_add_obj( yajl_gen gen, const char *db,const char* ptype
yajl_add_string_val(gen, "obj_type",ptype);
}
//void Audit_file_handler::print_sleep (THD *thd, int delay_ms)
//{
//
// unsigned long thdid = thd_get_thread_id(thd);
// yajl_gen gen = yajl_gen_alloc(&config, NULL);
// yajl_gen_array_open(gen);
// yajl_gen_map_open(gen);
// yajl_add_string_val(gen, "msg-type", "activity");
// uint64 ts = my_getsystime() / (10000);
// yajl_add_uint64(gen, "date", ts);
// yajl_add_uint64(gen, "thread-id", thdid);
// yajl_add_uint64(gen, "audit is going to sleep for ", delay_ms);
// yajl_gen_map_close(gen);
// yajl_gen_array_close(gen);
// fflush(m_log_file);
// int fd = fileno(m_log_file);
// my_sync(fd, MYF(MY_WME));
//
//}
//will return a pointer to the query and set len with the length of the query
//starting with MySQL version 5.1.41 thd_query_string is added
#if MYSQL_VERSION_ID > 50140
static const char * thd_query_str(THD * thd, size_t * len)
{
MYSQL_LEX_STRING * str = thd_query_string(thd);
if(str)
{
*len = str->length;
return str->str;
}
*len = 0;
return NULL;
}
#else
//we are being compiled against mysql version 5.1.40 or lower (our default compilation env)
//we still want to support thd_query_string if we are run on a version higher than 5.1.40, so we try to lookup the symbol
static LEX_STRING * (*thd_query_string_func)(THD *thd) = (LEX_STRING*(*)(THD*))dlsym(RTLD_DEFAULT, "thd_query_string");
static bool print_thd_query_string_func = true; //debug info print only once
static const char * thd_query_str(THD * thd, size_t * len)
{
if(print_thd_query_string_func)
{
sql_print_information("%s thd_query_string_func: 0x%lx", AUDIT_LOG_PREFIX, (unsigned long)thd_query_string_func);
print_thd_query_string_func = false;
}
if(thd_query_string_func)
{
MYSQL_LEX_STRING * str = thd_query_string_func(thd);
if(str)
{
*len = str->length;
return str->str;
}
*len = 0;
return NULL;
}
*len = thd->query_length;
return thd->query;
}
#endif
ssize_t Audit_json_formatter::event_format(ThdSesData* pThdData, IWriter * writer)
{
unsigned long thdid = thd_get_thread_id(pThdData->getTHD());
@ -452,7 +476,7 @@ ssize_t Audit_json_formatter::event_format(ThdSesData* pThdData, IWriter * write
size_t qlen = 0;
const char * query = thd_query(pThdData->getTHD(), &qlen);
const char * query = thd_query_str(pThdData->getTHD(), &qlen);
if (query && qlen > 0)
{
CHARSET_INFO *col_connection = Item::default_charset();

View File

@ -111,6 +111,8 @@ static const ThdOffsets thd_offsets_arr[] =
{"5.1.63-community","0f4d7e3b17eb36f17aafe4360993a769", 6328, 6392, 3688, 3960, 88, 2048},
//offsets for: /mysqlrpm/5.1.65/usr/sbin/mysqld (5.1.65-community)
{"5.1.65-community","4df4c0dfe11913bd1ef2bb3a6bc7a40e", 6376, 6440, 3736, 4008, 88, 2056},
//offsets for: /mysqlrpm/5.1.66/usr/sbin/mysqld (5.1.66-community)
{"5.1.66-community","544ed94102b82425e7592e7d7474fce4", 6376, 6440, 3736, 4008, 88, 2056},
//offsets for: mysqlrpm/5.5.8/usr/sbin/mysqld (5.5.8)
{"5.5.8","70a882693d54df8ab7c7d9f256e317bb", 6032, 6080, 3776, 4200, 88, 2560},
@ -142,7 +144,7 @@ static const ThdOffsets thd_offsets_arr[] =
{"5.5.20","9f6122576930c5d09ca9244094c83f24", 6048, 6096, 3800, 4224, 88, 2560},
//offsets for: mysqlrpm/5.5.21/usr/sbin/mysqld (5.5.21)
{"5.5.21","4a03ad064ed393dabdde175f3ea05ff2", 6048, 6096, 3800, 4224, 88, 2560},
//offsets for percons: /usr/sbin/mysqld (5.5.21-55)
//offsets for percona rpm (redhat 6): /usr/sbin/mysqld (5.5.21-55)
{"5.5.21-55","e4f1b39e9dca4edc51b8eb6aa09e2fa4", 6464, 6512, 4072, 4512, 88, 2576},
//offsets for: mysqlrpm/5.5.22/usr/sbin/mysqld (5.5.22)
{"5.5.22","f3592147108e65d92cb18fb4d900c4ab", 6048, 6096, 3800, 4224, 88, 2560},
@ -156,6 +158,8 @@ static const ThdOffsets thd_offsets_arr[] =
{"5.5.25a","b59c03244daf51d4327409288d8c889f", 6056, 6104, 3808, 4232, 88, 2568},
//offsets for: /mysqlrpm/5.5.27/usr/sbin/mysqld (5.5.27)
{"5.5.27","8a3bd2ea1db328f4443fc25a79450ff3", 6056, 6104, 3808, 4232, 88, 2568},
//offsets for: /mysqlrpm/5.5.28/usr/sbin/mysqld (5.5.28)
{"5.5.28","588a710a1aec3043203261af72a13219", 6056, 6104, 3808, 4232, 88, 2568},
@ -543,6 +547,8 @@ static my_bool json_file_handler_flush = FALSE;
static my_bool json_socket_handler_enable = FALSE;
static my_bool uninstall_plugin_enable = FALSE;
static my_bool validate_checksum_enable = FALSE;
static my_bool offsets_by_version_enable = FALSE;
static my_bool validate_offsets_extended_enable = FALSE;
static char * offsets_string = NULL;
static char * checksum_string = NULL;
static int delay_ms_val =0;
@ -1060,23 +1066,41 @@ static bool validate_offsets(const ThdOffsets * offset)
char buf[32*1024] = {0};
THD * thd = (THD *)buf;
//sanity check that offsets match
//we can also consider using secutiry context function to do some sanity checks
// char buffer[2048];
// thd_security_context(thd, buffer, 2048, 2000);
// fprintf(log_file, "info from security context: %s\n", buffer);
//we set the thread id to a value using the offset and then check that the value matches what thd_get_thread_id returns
const my_thread_id test_val = 123456;
(*(my_thread_id *) (((char *) thd)+ offset->thread_id)) = test_val;
const my_thread_id thread_id_test_val = 123456;
(*(my_thread_id *) (((char *) thd)+ offset->thread_id)) = thread_id_test_val;
my_thread_id res= thd_get_thread_id(thd);
bool retval = res == test_val;
if (!retval)
if (res != thread_id_test_val)
{
sql_print_error(
"%s Offsets version: %s match thread validation check fails with value: %lu. Skipping offest.",
log_prefix, offset->version, res);
"%s Offsets: %s (%s) match thread validation check fails with value: %lu. Skipping offest.",
log_prefix, offset->version, offset->md5digest, res);
return false;
}
return retval;
//extended validation via security_context method
//can be disabled via: validate_offests_extended
if(validate_offsets_extended_enable)
{
const query_id_t query_id_test_val = 789;
(*(query_id_t *) (((char *) thd)+ offset->query_id)) = query_id_test_val;
Security_context * sctx = (Security_context *) (((unsigned char *) thd) + offset->main_security_ctx);
char user_test_val[] = "aud_tusr";
sctx->user = user_test_val;
char buffer[2048] = {0};
thd_security_context(thd, buffer, 2048, 1000);
//verfiy our buffer contains query id
if(strstr(buffer, " 789") == NULL || strstr(buffer, user_test_val) == NULL)
{
sql_print_error(
"%s Offsets: %s (%s) sec context validation check fails with value: %s. Skipping offest.",
log_prefix, offset->version, offset->md5digest, buffer);
return false;
}
sql_print_information(
"%s extended offsets validate res: %s", log_prefix, buffer);
}
return true;
}
/**
@ -1087,57 +1111,60 @@ static bool validate_offsets(const ThdOffsets * offset)
static int setup_offsets()
{
DBUG_ENTER("setup_offsets");
sql_print_information ("%s setup_offsets audit_offsets: %s",log_prefix, offsets_string);
sql_print_information ("%s setup_offsets audit_offsets: %s validate_checksum: %d offsets_by_version: %d",
log_prefix, offsets_string, validate_checksum_enable, offsets_by_version_enable);
unsigned char digest[16] = {0};
char digest_str [128] = {0};
const ThdOffsets * offset;
//if present in my.cnf
//[mysqld]
//audit_validate_checksum=1
// or if
//audit_checksum=0f4d7e3b17eb36f17aafe4360993a769
//need to calculate digest
if (validate_checksum_enable || (checksum_string != NULL && strlen(checksum_string) > 0))
//setup digest_str to contain the md5sum in hex
my_MD5Context context;
my_MD5Init(&context);
const size_t buff_size = 16384;
unsigned char file_buff[buff_size] = {0};
MY_STAT stat_arg;
File fd;
if ((fd = my_open(my_progname, O_RDONLY, MYF(MY_WME))) > 0)
{
//setup digest_str to contain the md5sum in hex
sql_print_information(
"%s Validate checksum enabled. Mysqld %s ",
log_prefix, my_progname);
my_MD5Context context;
my_MD5Init(&context);
unsigned char * file_buff;
MY_STAT stat_arg;
File fd;
if (my_stat(my_progname, &stat_arg, MYF(MY_WME)))
ssize_t res;
do
{
if ((fd = my_open(my_progname, O_RDONLY,
MYF(MY_WME))) > 0)
res = read(fd, file_buff, buff_size);
if(res > 0)
{
file_buff = (unsigned char*) my_malloc(
(uint) stat_arg.st_size, MYF (MY_WME));
if (read(fd, (char*) file_buff,
(uint) stat_arg.st_size) >= 0L)
{
my_MD5Update(&context, file_buff,
stat_arg.st_size);
my_MD5Final(digest, &context);
}
(void) my_close(fd, MYF(0));
#if MYSQL_VERSION_ID > 50505
my_free(file_buff);
#else
my_free(file_buff, MYF(0));
#endif
my_MD5Update(&context, file_buff, res);
}
}
for (int j = 0; j < 16; j++)
while(res > 0);
if(res == 0) //reached end of file
{
sprintf(&(digest_str[j * 2]), "%02x", digest[j]);
my_MD5Final(digest, &context);
for (int j = 0; j < 16; j++)
{
sprintf(&(digest_str[j * 2]), "%02x", digest[j]);
}
}
else
{
sql_print_error("%s Failed program read. res: %d, errno: %d.",
log_prefix, res, errno);
}
(void) my_close(fd, MYF(0));
}
sql_print_information(
"%s mysql: %s (%s) ", log_prefix, my_progname, digest_str);
//if present in my.cnf
//[mysqld]
//audit_validate_checksum=1
// or if
//audit_checksum=0f4d7e3b17eb36f17aafe4360993a769
//if (validate_checksum_enable || (checksum_string != NULL && strlen(checksum_string) > 0))
//{
//if present the offset_string specified in my.cnf
//[mysqld]
//audit_offsets=6200, 6264, 3672, 3944, 88, 2048
@ -1181,62 +1208,71 @@ static int setup_offsets()
}
size_t arr_size = (sizeof(thd_offsets_arr) / sizeof(thd_offsets_arr[0]));
//iterate and search for the first offset which matches the version
for(int i=0; i < arr_size; i++)
//iterate and search for the first offset which matches our checksum
if(validate_checksum_enable && strlen(digest_str) > 0)
{
offset = thd_offsets_arr + i;
//if present in my.cnf
//[mysqld]
//audit_validate_checksum=1
//plugin-load=AUDIT=libaudit_plugin.so
if (validate_checksum_enable && strlen (offset->md5digest) >0)
{
int kd=0;
if (!strncasecmp(digest_str, offset->md5digest, 32))
{
sql_print_information ("%s Checksum is %s verified", log_prefix, digest_str);
sql_print_information("%s Using offsets from offset version: %s", log_prefix, offset->version);
Audit_formatter::thd_offsets = *offset;
DBUG_RETURN(0);
//return
}
}
for(int i=0; i < arr_size; i++)
{
offset = thd_offsets_arr + i;
if (strlen(offset->md5digest) >0)
{
if (!strncasecmp(digest_str, offset->md5digest, 32))
{
sql_print_information("%s Checksum verified. Using offsets from offset version: %s (%s)", log_prefix, offset->version, digest_str);
Audit_formatter::thd_offsets = *offset;
DBUG_RETURN(0);
//return
}
}
}
}
if(offsets_by_version_enable)
{
for(int i=0; i < arr_size; i++)
{
offset = thd_offsets_arr + i;
const char * version = offset->version;
const char * dash = strchr(version, '-');
char version_stripped[16] = {0};
if(dash) //we use the version string up to the '-'
{
size_t tocopy = dash - version;
if(tocopy > 15) tocopy = 15; //sanity
strncpy(version_stripped, version, tocopy);
version = version_stripped;
}
if(strstr(server_version, version))
{
if (validate_offsets(offset))
{
sql_print_information("%s Using offsets from offset version: %s (%s)", log_prefix, offset->version, offset->md5digest);
Audit_formatter::thd_offsets = *offset;
DBUG_RETURN(0);
}
else
{
//try doing 24 byte decrement on THD offsets. Seen that on Ubuntu/Debian this is valid.
OFFSET dec = 24;
ThdOffsets decoffsets = *offset;
decoffsets.query_id -= dec;
decoffsets.thread_id -= dec;
decoffsets.main_security_ctx -= dec;
decoffsets.command -= dec;
if (validate_offsets(&decoffsets))
{
Audit_formatter::thd_offsets = decoffsets;
sql_print_information("%s Using decrement (%d) offsets from offset version: %s (%s) values: %d %d %d %d %d %d", log_prefix, dec, offset->version, offset->md5digest,
Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id,
Audit_formatter::thd_offsets.main_security_ctx,
Audit_formatter::thd_offsets.command,
Audit_formatter::thd_offsets.lex,
Audit_formatter::thd_offsets.lex_comment);
//if present in my.cnf
//[mysqld]
//audit_validate_checksum=0
//plugin-load=AUDIT=libaudit_plugin.so
if(!validate_checksum_enable && (strstr(server_version, offset->version)))
{
if (validate_offsets(offset))
{
sql_print_information("%s Using offsets from offset version: %s, digest: %s", log_prefix, offset->version, offset->md5digest);
Audit_formatter::thd_offsets = *offset;
DBUG_RETURN(0);
}
else
{
//try doing 24 byte decrement on THD offsets. Seen that on Ubuntu/Debian this is valid.
OFFSET dec = 24;
ThdOffsets decoffsets = *offset;
decoffsets.query_id -= dec;
decoffsets.thread_id -= dec;
decoffsets.main_security_ctx -= dec;
decoffsets.command -= dec;
if (validate_offsets(&decoffsets))
{
Audit_formatter::thd_offsets = decoffsets;
sql_print_information("%s Using decrement (%d) offsets from offset version: %s, digest: %s values: %d %d %d %d %d %d", log_prefix, dec, offset->version, offset->md5digest,
Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id,
Audit_formatter::thd_offsets.main_security_ctx,
Audit_formatter::thd_offsets.command,
Audit_formatter::thd_offsets.lex,
Audit_formatter::thd_offsets.lex_comment);
DBUG_RETURN(0);
}
}
DBUG_RETURN(0);
}
}
}
}
}
@ -1713,10 +1749,19 @@ static MYSQL_SYSVAR_BOOL(uninstall_plugin, uninstall_plugin_enable,
"AUDIT uninstall plugin Enable|Disable. Default disabled. If disabled attempts to uninstall the AUDIT plugin via the sql UNINSTALL command will fail.", NULL, NULL, 0);
static MYSQL_SYSVAR_BOOL(offsets_by_version, offsets_by_version_enable,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY ,
"AUDIT plugin search offsets by version. If checksum validation doesn't pass will attempt to load and validate offsets according to version. Enable|Disable", NULL, NULL, 1);
static MYSQL_SYSVAR_BOOL(validate_checksum, validate_checksum_enable,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY ,
"AUDIT plugin binary checksum validation Enable|Disable", NULL, NULL, 1);
static MYSQL_SYSVAR_BOOL(validate_offsets_extended, validate_offsets_extended_enable,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY ,
"AUDIT plugin offset extended validation Enable|Disable", NULL, NULL, 1);
static MYSQL_SYSVAR_BOOL(json_socket, json_socket_handler_enable,
PLUGIN_VAR_RQCMDARG,
"AUDIT plugin json log unix socket Enable|Disable", NULL, json_log_socket_enable, 0);
@ -1750,6 +1795,8 @@ static struct st_mysql_sys_var* audit_system_variables[] =
MYSQL_SYSVAR(json_file_flush),
MYSQL_SYSVAR(uninstall_plugin),
MYSQL_SYSVAR(validate_checksum),
MYSQL_SYSVAR(offsets_by_version),
MYSQL_SYSVAR(validate_offsets_extended),
MYSQL_SYSVAR(json_socket_name),
MYSQL_SYSVAR(offsets),
MYSQL_SYSVAR(json_socket),