Add MariaDB 10.1.13 offsets. Format the code.

pull/141/head
Arnold Robbins 2016-03-31 11:55:57 +03:00
parent 051b1fd67e
commit 80646620e9
9 changed files with 3683 additions and 3626 deletions

View File

@ -49,12 +49,12 @@ typedef struct _THDPRINTED {
} THDPRINTED; } THDPRINTED;
#define MAX_COMMAND_CHAR_NUMBERS 40 #define MAX_COMMAND_CHAR_NUMBERS 40
const char * retrieve_command (THD * thd, bool & is_sql_cmd); const char *retrieve_command(THD *thd, bool & is_sql_cmd);
typedef size_t OFFSET; typedef size_t OFFSET;
#define MAX_COM_STATUS_VARS_RECORDS 512 #define MAX_COM_STATUS_VARS_RECORDS 512
//mysql max identifier is 64 so 2*64 + . and null // mysql max identifier is 64 so 2*64 + . and null
#define MAX_OBJECT_CHAR_NUMBERS 131 #define MAX_OBJECT_CHAR_NUMBERS 131
#define MAX_USER_CHAR_NUMBERS 20 #define MAX_USER_CHAR_NUMBERS 20
#define MAX_NUM_OBJECT_ELEM 256 #define MAX_NUM_OBJECT_ELEM 256
@ -63,22 +63,21 @@ typedef size_t OFFSET;
/** /**
* The struct used to hold offsets. We should have one per version. * The struct used to hold offsets. We should have one per version.
*/ */
typedef struct ThdOffsets typedef struct ThdOffsets {
{ const char *version;
const char * version; const char *md5digest;
const char * md5digest; OFFSET query_id;
OFFSET query_id; OFFSET thread_id;
OFFSET thread_id; OFFSET main_security_ctx;
OFFSET main_security_ctx; OFFSET command;
OFFSET command;
OFFSET lex; OFFSET lex;
OFFSET lex_comment; OFFSET lex_comment;
OFFSET sec_ctx_user; OFFSET sec_ctx_user;
OFFSET sec_ctx_host; OFFSET sec_ctx_host;
OFFSET sec_ctx_ip; OFFSET sec_ctx_ip;
OFFSET sec_ctx_priv_user; OFFSET sec_ctx_priv_user;
OFFSET db; OFFSET db;
OFFSET killed; OFFSET killed;
} ThdOffsets; } ThdOffsets;
/* /*
@ -96,267 +95,263 @@ typedef ssize_t (*audit_write_func)(const char *, size_t);
/** /**
* Interface for an io writer * Interface for an io writer
*/ */
class IWriter class IWriter {
{
public: public:
virtual ~IWriter() {} virtual ~IWriter() {}
//return negative on fail // return negative on fail
virtual ssize_t write(const char * data, size_t size) = 0; virtual ssize_t write(const char *data, size_t size) = 0;
inline ssize_t write_str(const char * str) inline ssize_t write_str(const char *str)
{ {
return write(str, strlen(str)); return write(str, strlen(str));
} }
//return 0 on success // return 0 on success
virtual int open(const char * io_dest, bool log_errors) = 0; virtual int open(const char *io_dest, bool log_errors) = 0;
virtual void close() = 0; virtual void close() = 0;
}; };
class ThdSesData { class ThdSesData {
public: public:
// enum indicating from where the object list came from
enum ObjectIterType {OBJ_NONE, OBJ_DB, OBJ_QUERY_CACHE, OBJ_TABLE_LIST};
ThdSesData(THD *pTHD);
THD *getTHD() { return m_pThd;}
const char *getCmdName() { return m_CmdName; }
const char *getUserName() { return m_UserName; }
/**
* Start fetching objects. Return true if there are objects available.
*/
bool startGetObjects();
/**
* Get next object. Return true if populated. False if there isn't an object available.
* Will point the passed pointers to point to db, name and type.
* obj_type is optional and may be null.
*/
bool getNextObject(const char **db_name, const char **obj_name, const char **obj_type);
//enum indicating from where the object list came from
enum ObjectIterType {OBJ_NONE, OBJ_DB, OBJ_QUERY_CACHE, OBJ_TABLE_LIST};
ThdSesData(THD *pTHD);
THD* getTHD () { return m_pThd;}
const char * getCmdName () { return m_CmdName; }
const char * getUserName () { return m_UserName; }
/**
* Start fetching objects. Return true if there are objects available.
*/
bool startGetObjects();
/**
* Get next object. Return true if populated. False if there isn't an object available.
* Will point the passed pointers to point to db, name and type.
* obj_type is optional and may be null.
*/
bool getNextObject(const char ** db_name, const char ** obj_name, const char ** obj_type);
private: private:
THD *m_pThd; THD *m_pThd;
const char *m_CmdName; const char *m_CmdName;
const char *m_UserName; const char *m_UserName;
bool m_isSqlCmd; bool m_isSqlCmd;
enum ObjectIterType m_objIterType; enum ObjectIterType m_objIterType;
//pointer for iterating tables // pointer for iterating tables
TABLE_LIST * m_tables; TABLE_LIST *m_tables;
//indicator if we are at the first table // indicator if we are at the first table
bool m_firstTable; bool m_firstTable;
//used for query cache iter // used for query cache iter
QueryTableInf * m_tableInf; QueryTableInf *m_tableInf;
int m_index; int m_index;
protected: protected:
ThdSesData (const ThdSesData& ); ThdSesData(const ThdSesData&);
ThdSesData &operator =(const ThdSesData& ); ThdSesData &operator =(const ThdSesData&);
}; };
/** /**
* Base for audit formatter * Base for audit formatter
*/ */
class Audit_formatter class Audit_formatter {
{
public: public:
virtual ~Audit_formatter() {}
virtual ~Audit_formatter() {} /**
* static offsets to use for fetching THD data. Set by the audit plugin during startup.
*/
static ThdOffsets thd_offsets;
/** /**
* static offsets to use for fetching THD data. Set by the audit plugin during startup. * Format an audit event from the passed THD. Will write out its output using the audit_write_func.
*/ *
static ThdOffsets thd_offsets; * @return -1 on a failure
*/
virtual ssize_t event_format(ThdSesData *pThdData, IWriter *writer) =0;
/**
* format a message when handler is started
* @return -1 on a failure
*/
virtual ssize_t start_msg_format(IWriter *writer) { return 0; }
/**
* format a message when handler is stopped
* @return -1 on a failure
*/
virtual ssize_t stop_msg_format(IWriter *writer) { return 0; }
/** static const char *retrieve_object_type(TABLE_LIST *pObj);
* Format an audit event from the passed THD. Will write out its output using the audit_write_func. static QueryTableInf *getQueryCacheTableList1(THD *thd);
* // utility functions for fetching thd stuff
* @return -1 on a failure static inline my_thread_id thd_inst_thread_id(THD *thd)
*/ {
virtual ssize_t event_format(ThdSesData *pThdData, IWriter * writer) =0; return *(my_thread_id *) (((unsigned char *) thd)
/** + Audit_formatter::thd_offsets.thread_id);
* format a message when handler is started }
* @return -1 on a failure static inline query_id_t thd_inst_query_id(THD *thd)
*/ {
virtual ssize_t start_msg_format(IWriter * writer) { return 0; } return *(query_id_t *) (((unsigned char *) thd)
/** + Audit_formatter::thd_offsets.query_id);
* format a message when handler is stopped }
* @return -1 on a failure static inline Security_context *thd_inst_main_security_ctx(THD *thd)
*/ {
virtual ssize_t stop_msg_format(IWriter * writer) { return 0; } return (Security_context *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.main_security_ctx);
}
static const char * retrieve_object_type (TABLE_LIST *pObj); static inline const char *thd_db(THD *thd)
static QueryTableInf* getQueryCacheTableList1 (THD *thd); {
//utility functions for fetching thd stuff if (! Audit_formatter::thd_offsets.db) // no offsets use compiled in header
static inline my_thread_id thd_inst_thread_id(THD * thd)
{
return *(my_thread_id *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.thread_id);
}
static inline query_id_t thd_inst_query_id(THD * thd)
{
return *(query_id_t *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.query_id);
}
static inline Security_context * thd_inst_main_security_ctx(THD * thd)
{
return (Security_context *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.main_security_ctx);
}
static inline const char * thd_db(THD * thd)
{
if(!Audit_formatter::thd_offsets.db) //no offsets use compiled in header
{ {
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709 #if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
return thd->db; return thd->db;
#else #else
return thd->db().str; return thd->db().str;
#endif #endif
} }
return *(const char **) (((unsigned char *) thd) return *(const char **) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.db); + Audit_formatter::thd_offsets.db);
} }
static inline int thd_killed(THD * thd) static inline int thd_killed(THD *thd)
{ {
if(!Audit_formatter::thd_offsets.killed) //no offsets use thd_killed function if (! Audit_formatter::thd_offsets.killed) // no offsets use thd_killed function
{ {
return ::thd_killed(thd); return ::thd_killed(thd);
} }
return *(int *) (((unsigned char *) thd) return *(int *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.killed); + Audit_formatter::thd_offsets.killed);
} }
static inline const char * thd_inst_main_security_ctx_user(THD * thd) static inline const char *thd_inst_main_security_ctx_user(THD *thd)
{ {
Security_context * sctx = thd_inst_main_security_ctx(thd); Security_context *sctx = thd_inst_main_security_ctx(thd);
if(!Audit_formatter::thd_offsets.sec_ctx_user) //no offsets use compiled in header if (! Audit_formatter::thd_offsets.sec_ctx_user) // no offsets use compiled in header
{ {
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709 #if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
return sctx->user; return sctx->user;
#else #else
return sctx->user().str; return sctx->user().str;
#endif #endif
} }
return *(const char **) (((unsigned char *) sctx) return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_user); + Audit_formatter::thd_offsets.sec_ctx_user);
} }
static inline const char * thd_inst_main_security_ctx_host(THD * thd) static inline const char *thd_inst_main_security_ctx_host(THD *thd)
{ {
Security_context * sctx = thd_inst_main_security_ctx(thd); Security_context *sctx = thd_inst_main_security_ctx(thd);
if(!Audit_formatter::thd_offsets.sec_ctx_ip) //check ip to understand if set as host is first and may actually be set to 0 if (! Audit_formatter::thd_offsets.sec_ctx_ip) // check ip to understand if set as host is first and may actually be set to 0
{ {
//interface changed in 5.5.34 and 5.6.14 and up host changed to get_host() // interface changed in 5.5.34 and 5.6.14 and up host changed to get_host()
//see: http://bazaar.launchpad.net/~mysql/mysql-server/5.5/revision/4407.1.1/sql/sql_class.h // see: http://bazaar.launchpad.net/~mysql/mysql-server/5.5/revision/4407.1.1/sql/sql_class.h
#if defined(MARIADB_BASE_VERSION) #if defined(MARIADB_BASE_VERSION)
return sctx->host; return sctx->host;
#else #else
// MySQL // MySQL
#if MYSQL_VERSION_ID < 50534 || (MYSQL_VERSION_ID >= 50600 && MYSQL_VERSION_ID < 50614) #if MYSQL_VERSION_ID < 50534 || (MYSQL_VERSION_ID >= 50600 && MYSQL_VERSION_ID < 50614)
return sctx->host; return sctx->host;
#elif (MYSQL_VERSION_ID >= 50534 && MYSQL_VERSION_ID < 50600) \ #elif (MYSQL_VERSION_ID >= 50534 && MYSQL_VERSION_ID < 50600) \
|| (MYSQL_VERSION_ID >= 50614 && MYSQL_VERSION_ID < 50709) || (MYSQL_VERSION_ID >= 50614 && MYSQL_VERSION_ID < 50709)
return sctx->get_host()->ptr(); return sctx->get_host()->ptr();
#else #else
// interface changed again in 5.7 // interface changed again in 5.7
return sctx->host().str; return sctx->host().str;
#endif #endif
#endif // ! defined(MARIADB_BASE_VERSION) #endif // ! defined(MARIADB_BASE_VERSION)
} }
return *(const char **) (((unsigned char *) sctx) return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_host); + Audit_formatter::thd_offsets.sec_ctx_host);
} }
static inline const char * thd_inst_main_security_ctx_ip(THD * thd) static inline const char *thd_inst_main_security_ctx_ip(THD *thd)
{ {
Security_context * sctx = thd_inst_main_security_ctx(thd); Security_context *sctx = thd_inst_main_security_ctx(thd);
if(!Audit_formatter::thd_offsets.sec_ctx_ip) //no offsets use compiled in header if (! Audit_formatter::thd_offsets.sec_ctx_ip) // no offsets use compiled in header
{ {
//interface changed in 5.5.34 and 5.6.14 and up host changed to get_ip() // interface changed in 5.5.34 and 5.6.14 and up host changed to get_ip()
#if defined(MARIADB_BASE_VERSION) #if defined(MARIADB_BASE_VERSION)
return sctx->ip; return sctx->ip;
#else #else
// MySQL // MySQL
#if MYSQL_VERSION_ID < 50534 || (MYSQL_VERSION_ID >= 50600 && MYSQL_VERSION_ID < 50614) #if MYSQL_VERSION_ID < 50534 || (MYSQL_VERSION_ID >= 50600 && MYSQL_VERSION_ID < 50614)
return sctx->ip; return sctx->ip;
#elif (MYSQL_VERSION_ID >= 50534 && MYSQL_VERSION_ID < 50600) \ #elif (MYSQL_VERSION_ID >= 50534 && MYSQL_VERSION_ID < 50600) \
|| (MYSQL_VERSION_ID >= 50614 && MYSQL_VERSION_ID < 50709) || (MYSQL_VERSION_ID >= 50614 && MYSQL_VERSION_ID < 50709)
return sctx->get_ip()->ptr(); return sctx->get_ip()->ptr();
#else #else
// interface changed again in 5.7 // interface changed again in 5.7
return sctx->ip().str; return sctx->ip().str;
#endif #endif
#endif // ! defined(MARIADB_BASE_VERSION) #endif // ! defined(MARIADB_BASE_VERSION)
} }
return *(const char **) (((unsigned char *) sctx) return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_ip); + Audit_formatter::thd_offsets.sec_ctx_ip);
} }
static inline const char * thd_inst_main_security_ctx_priv_user(THD * thd) static inline const char *thd_inst_main_security_ctx_priv_user(THD *thd)
{ {
Security_context * sctx = thd_inst_main_security_ctx(thd); Security_context *sctx = thd_inst_main_security_ctx(thd);
if(!Audit_formatter::thd_offsets.sec_ctx_priv_user) //no offsets use compiled in header if (! Audit_formatter::thd_offsets.sec_ctx_priv_user) // no offsets use compiled in header
{ {
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709 #if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
return sctx->priv_user; return sctx->priv_user;
#else #else
return sctx->priv_user().str; return sctx->priv_user().str;
#endif #endif
} }
#if MYSQL_VERSION_ID < 50505 #if MYSQL_VERSION_ID < 50505
//in 5.1.x priv_user is a pointer // in 5.1.x priv_user is a pointer
return *(const char **) (((unsigned char *) sctx) return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_priv_user); + Audit_formatter::thd_offsets.sec_ctx_priv_user);
#else #else
//in 5.5 and up priv_user is an array (char priv_user[USERNAME_LENGTH]) // in 5.5 and up priv_user is an array (char priv_user[USERNAME_LENGTH])
return (const char *) (((unsigned char *) sctx) return (const char *) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_priv_user); + Audit_formatter::thd_offsets.sec_ctx_priv_user);
#endif #endif
} }
static inline int thd_inst_command(THD * thd) static inline int thd_inst_command(THD *thd)
{ {
return *(int *) (((unsigned char *) thd) + Audit_formatter::thd_offsets.command); return *(int *) (((unsigned char *) thd) + Audit_formatter::thd_offsets.command);
} }
static inline LEX* thd_lex(THD * thd) static inline LEX *thd_lex(THD *thd)
{ {
return *(LEX**) (((unsigned char *) thd) + Audit_formatter::thd_offsets.lex); return *(LEX **) (((unsigned char *) thd) + Audit_formatter::thd_offsets.lex);
} }
//we don't use get_db_name() as when we call it view may be not null and it may return an invalid value for view_db // we don't use get_db_name() as when we call it view may be not null and it may return an invalid value for view_db
static inline const char * table_get_db_name(TABLE_LIST * table) static inline const char *table_get_db_name(TABLE_LIST *table)
{ {
return table->db; return table->db;
} }
static inline const char * table_get_name(TABLE_LIST * table) static inline const char *table_get_name(TABLE_LIST *table)
{ {
return table->table_name; return table->table_name;
} }
static inline bool table_is_view(TABLE_LIST * table) static inline bool table_is_view(TABLE_LIST *table)
{ {
return table->view_tables != 0; return table->view_tables != 0;
} }
}; };
/** /**
* Format the audit even in json format * Format the audit even in json format
*/ */
class Audit_json_formatter: public Audit_formatter class Audit_json_formatter: public Audit_formatter {
{
public: public:
static const char *DEF_MSG_DELIMITER;
static const char * DEF_MSG_DELIMITER; Audit_json_formatter(): m_msg_delimiter(NULL), m_write_start_msg(true), m_password_mask_regex_preg(NULL),
Audit_json_formatter(): m_msg_delimiter(NULL), m_write_start_msg(true), m_password_mask_regex_preg(NULL),
m_password_mask_regex_compiled(false), m_perform_password_masking(NULL) m_password_mask_regex_compiled(false), m_perform_password_masking(NULL)
{
config.beautify = 0;
config.indentString = NULL;
}
virtual ~Audit_json_formatter()
{ {
if(m_password_mask_regex_preg) config.beautify = 0;
config.indentString = NULL;
}
virtual ~Audit_json_formatter()
{
if (m_password_mask_regex_preg)
{ {
m_password_mask_regex_compiled = false; m_password_mask_regex_compiled = false;
pcre_free(m_password_mask_regex_preg); pcre_free(m_password_mask_regex_preg);
@ -364,20 +359,20 @@ public:
} }
} }
virtual ssize_t event_format(ThdSesData *pThdData, IWriter * writer); virtual ssize_t event_format(ThdSesData *pThdData, IWriter *writer);
virtual ssize_t start_msg_format(IWriter * writer); virtual ssize_t start_msg_format(IWriter *writer);
/** /**
* Utility method used to compile a regex program. Will compile and log errors if necessary. * Utility method used to compile a regex program. Will compile and log errors if necessary.
* Return null if fails * Return null if fails
*/ */
static pcre * regex_compile(const char * str); static pcre *regex_compile(const char *str);
/** /**
* Compile password masking regex * Compile password masking regex
* Return 0 on success * Return true on success
*/ */
int compile_password_masking_regex(const char * str); bool compile_password_masking_regex(const char *str);
/** /**
* Boolean indicating if to log start msg. * Boolean indicating if to log start msg.
@ -389,20 +384,20 @@ public:
/** /**
* Callback function to determine if password masking should be performed * Callback function to determine if password masking should be performed
*/ */
my_bool (* m_perform_password_masking)(const char *cmd); my_bool (*m_perform_password_masking)(const char *cmd);
/** /**
* Message delimiter. Should point to a valid json string (supporting the json escapping format). * Message delimiter. Should point to a valid json string (supporting the json escapping format).
* Will only be checked at the start. Public so can be set by sysvar. * Will only be checked at the start. Public so can be set by sysvar.
* *
* We only support a delimiter up to 32 chars * We only support a delimiter up to 32 chars
*/ */
char * m_msg_delimiter; char *m_msg_delimiter;
/** /**
* Configuration of yajl. Leave public so sysvar can update this directly. * Configuration of yajl. Leave public so sysvar can update this directly.
*/ */
yajl_gen_config config; yajl_gen_config config;
protected: protected:
@ -417,83 +412,79 @@ protected:
/** /**
* Regex used for password masking * Regex used for password masking
*/ */
pcre * m_password_mask_regex_preg; pcre *m_password_mask_regex_preg;
}; };
/** /**
* Base class for audit handlers. Provides basic locking setup. * Base class for audit handlers. Provides basic locking setup.
*/ */
class Audit_handler class Audit_handler {
{
public: public:
static const size_t MAX_AUDIT_HANDLERS_NUM = 4;
static const size_t JSON_FILE_HANDLER = 1;
static const size_t JSON_SOCKET_HANDLER = 3;
static Audit_handler *m_audit_handler_list[];
/**
* Will iterate the handler list and log using each handler
*/
static void log_audit_all(ThdSesData *pThdData);
static const size_t MAX_AUDIT_HANDLERS_NUM = 4; /**
static const size_t JSON_FILE_HANDLER = 1; * Will iterate the handler list and stop all handlers
static const size_t JSON_SOCKET_HANDLER = 3; */
static void stop_all();
static Audit_handler * m_audit_handler_list[]; Audit_handler() :
m_initialized(false), m_enabled(false), m_print_offset_err(true),
m_formatter(NULL), m_failed(false), m_log_io_errors(true)
{
}
/** virtual ~Audit_handler()
* Will iterate the handler list and log using each handler {
*/ if (m_initialized)
static void log_audit_all(ThdSesData *pThdData); {
rwlock_destroy(&LOCK_audit);
/**
* Will iterate the handler list and stop all handlers
*/
static void stop_all();
Audit_handler() :
m_initialized(false), m_enabled(false), m_print_offset_err(true), m_formatter(NULL), m_failed(false), m_log_io_errors(true)
{
}
virtual ~Audit_handler()
{
if (m_initialized)
{
rwlock_destroy(&LOCK_audit);
pthread_mutex_destroy(&LOCK_io); pthread_mutex_destroy(&LOCK_io);
} }
} }
/** /**
* Should be called to initialize. We don't init in constructor in order to provide indication if * Should be called to initialize. We don't init in constructor in order to provide indication if
* pthread stuff failed init. * pthread stuff failed init.
* *
* @frmt the formatter to use in this handler (does not manage distruction of this object) * @frmt the formatter to use in this handler (does not manage distruction of this object)
* @return 0 on success * @return 0 on success
*/ */
int init(Audit_formatter * frmt) int init(Audit_formatter *frmt)
{ {
m_formatter = frmt; m_formatter = frmt;
if (m_initialized) if (m_initialized)
{ {
return 0; return 0;
} }
int res = my_rwlock_init(&LOCK_audit, NULL); int res = my_rwlock_init(&LOCK_audit, NULL);
if (res) if (res)
{ {
return res; return res;
} }
res = pthread_mutex_init(&LOCK_io, MY_MUTEX_INIT_SLOW);; res = pthread_mutex_init(&LOCK_io, MY_MUTEX_INIT_SLOW);;
if (res) if (res)
{ {
return res; return res;
} }
m_initialized = true; m_initialized = true;
return res; return res;
} }
bool is_init() bool is_init()
{ {
return m_initialized; return m_initialized;
} }
void set_enable(bool val); void set_enable(bool val);
bool is_enabled() bool is_enabled()
{ {
@ -505,10 +496,10 @@ public:
*/ */
void flush(); void flush();
/** /**
* Will get relevant shared lock and call internal method of handler * Will get relevant shared lock and call internal method of handler
*/ */
void log_audit(ThdSesData *pThdData); void log_audit(ThdSesData *pThdData);
/** /**
* Public so can be configured via sysvar * Public so can be configured via sysvar
@ -516,16 +507,16 @@ public:
unsigned int m_retry_interval; unsigned int m_retry_interval;
protected: protected:
Audit_formatter * m_formatter; Audit_formatter *m_formatter;
virtual void handler_start(); virtual void handler_start();
//wiil call internal method and set failed as needed // wiil call internal method and set failed as needed
bool handler_start_nolock(); bool handler_start_nolock();
virtual void handler_stop(); virtual void handler_stop();
virtual bool handler_start_internal() = 0; virtual bool handler_start_internal() = 0;
virtual void handler_stop_internal() = 0; virtual void handler_stop_internal() = 0;
virtual bool handler_log_audit(ThdSesData *pThdData) =0; virtual bool handler_log_audit(ThdSesData *pThdData) =0;
bool m_initialized; bool m_initialized;
bool m_enabled; bool m_enabled;
bool m_failed; bool m_failed;
bool m_log_io_errors; bool m_log_io_errors;
time_t m_last_retry_sec_ts; time_t m_last_retry_sec_ts;
@ -538,154 +529,147 @@ protected:
inline bool is_failed_now() inline bool is_failed_now()
{ {
return m_failed && (m_retry_interval < 0 || return m_failed && (m_retry_interval < 0 ||
difftime(time(NULL), m_last_retry_sec_ts) > m_retry_interval); difftime(time(NULL), m_last_retry_sec_ts) > m_retry_interval);
} }
//override default assignment and copy to protect against creating additional instances // override default assignment and copy to protect against creating additional instances
Audit_handler & operator=(const Audit_handler&); Audit_handler & operator=(const Audit_handler&);
Audit_handler(const Audit_handler&); Audit_handler(const Audit_handler&);
private: private:
//bool indicating if to print offset errors to log or not // bool indicating if to print offset errors to log or not
bool m_print_offset_err; bool m_print_offset_err;
//lock io // lock io
pthread_mutex_t LOCK_io; pthread_mutex_t LOCK_io;
//audit (enable) lock // audit (enable) lock
rw_lock_t LOCK_audit; rw_lock_t LOCK_audit;
inline void lock_shared() inline void lock_shared()
{ {
rw_rdlock(&LOCK_audit); rw_rdlock(&LOCK_audit);
} }
inline void lock_exclusive() inline void lock_exclusive()
{ {
rw_wrlock(&LOCK_audit); rw_wrlock(&LOCK_audit);
} }
inline void unlock() inline void unlock()
{ {
rw_unlock(&LOCK_audit); rw_unlock(&LOCK_audit);
} }
}; };
/** /**
* Base class for handler which have io and need a lock * Base class for handler which have io and need a lock
*/ */
class Audit_io_handler: public Audit_handler, public IWriter class Audit_io_handler: public Audit_handler, public IWriter {
{
public: public:
Audit_io_handler() : m_io_dest(NULL), m_io_type(NULL) Audit_io_handler()
: m_io_dest(NULL), m_io_type(NULL)
{ {
} }
virtual ~Audit_io_handler() virtual ~Audit_io_handler()
{ {
} }
/** /**
* target we write to (socket/file). Public so we update via sysvar * target we write to (socket/file). Public so we update via sysvar
*/ */
char * m_io_dest; char *m_io_dest;
protected: protected:
virtual bool handler_start_internal(); virtual bool handler_start_internal();
virtual void handler_stop_internal(); virtual void handler_stop_internal();
//used for logging messages // used for logging messages
const char * m_io_type; const char *m_io_type;
}; };
class Audit_file_handler: public Audit_io_handler class Audit_file_handler: public Audit_io_handler {
{
public: public:
Audit_file_handler() : Audit_file_handler() :
m_sync_period(0), m_log_file(NULL), m_sync_counter(0), m_bufsize(0) m_sync_period(0), m_log_file(NULL), m_sync_counter(0), m_bufsize(0)
{ {
m_io_type = "file"; m_io_type = "file";
} }
virtual ~Audit_file_handler() virtual ~Audit_file_handler()
{ {
} }
/** /**
* The period to use for syncing to the file system. 0 means we don't sync. * The period to use for syncing to the file system. 0 means we don't sync.
* 1 means each write we sync. Larger than 1 means every sync_period we sync. * 1 means each write we sync. Larger than 1 means every sync_period we sync.
* *
* We leave this public so the mysql sysvar function can update this variable directly. * We leave this public so the mysql sysvar function can update this variable directly.
*/ */
unsigned int m_sync_period; unsigned int m_sync_period;
/** /**
* The buf size used by the file stream. 0 = use default, negative or 1 = no buffering * The buf size used by the file stream. 0 = use default, negative or 1 = no buffering
*/ */
long m_bufsize; long m_bufsize;
/** /**
* Write function we pass to formatter * Write function we pass to formatter
*/ */
ssize_t write(const char * data, size_t size); ssize_t write(const char *data, size_t size);
void close(); void close();
int open(const char * io_dest, bool m_log_errors); int open(const char *io_dest, bool m_log_errors);
//static void print_sleep (THD *thd, int delay_ms); // static void print_sleep(THD *thd, int delay_ms);
protected: protected:
//override default assignment and copy to protect against creating additional instances // override default assignment and copy to protect against creating additional instances
Audit_file_handler & operator=(const Audit_file_handler&); Audit_file_handler & operator=(const Audit_file_handler&);
Audit_file_handler(const Audit_file_handler&); Audit_file_handler(const Audit_file_handler&);
/**
* Will acquire locks and call handler_write
/** */
* Will acquire locks and call handler_write virtual bool handler_log_audit(ThdSesData *pThdData);
*/ FILE *m_log_file;
virtual bool handler_log_audit(ThdSesData *pThdData); // the period to use for syncing
FILE * m_log_file; unsigned int m_sync_counter;
//the period to use for syncing
unsigned int m_sync_counter;
}; };
class Audit_socket_handler: public Audit_io_handler class Audit_socket_handler: public Audit_io_handler {
{
public: public:
Audit_socket_handler() : Audit_socket_handler() :
m_vio(NULL), m_connect_timeout(1) m_vio(NULL), m_connect_timeout(1)
{ {
m_io_type = "socket"; m_io_type = "socket";
} }
virtual ~Audit_socket_handler() virtual ~Audit_socket_handler()
{ {
} }
/** /**
* Connect timeout in secconds * Connect timeout in secconds
*/ */
unsigned int m_connect_timeout; unsigned int m_connect_timeout;
/** /**
* Write function we pass to formatter * Write function we pass to formatter
*/ */
ssize_t write(const char * data, size_t size); ssize_t write(const char *data, size_t size);
void close(); void close();
int open(const char * io_dest, bool log_errors); int open(const char *io_dest, bool log_errors);
protected: protected:
//override default assignment and copy to protect against creating additional instances // override default assignment and copy to protect against creating additional instances
Audit_socket_handler & operator=(const Audit_socket_handler&); Audit_socket_handler & operator=(const Audit_socket_handler&);
Audit_socket_handler(const Audit_socket_handler&); Audit_socket_handler(const Audit_socket_handler&);
/** /**
* Will acquire locks and call handler_write * Will acquire locks and call handler_write
*/ */
virtual bool handler_log_audit(ThdSesData *pThdData); virtual bool handler_log_audit(ThdSesData *pThdData);
//Vio we write to // Vio we write to
//define as void* so we don't access members directly // define as void* so we don't access members directly
void * m_vio; void *m_vio;
}; };
#endif /* AUDIT_HANDLER_H_ */ #endif /* AUDIT_HANDLER_H_ */

4
include/md5.h Executable file → Normal file
View File

@ -43,13 +43,13 @@ extern void MD5_Init(MD5_CTX *ctx);
extern void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size); extern void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size);
extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
//define the my_MD5* functions // define the my_MD5* functions
#define my_MD5Context MD5_CTX #define my_MD5Context MD5_CTX
#define my_MD5Init MD5_Init #define my_MD5Init MD5_Init
#define my_MD5Update MD5_Update #define my_MD5Update MD5_Update
#define my_MD5Final MD5_Final #define my_MD5Final MD5_Final
#endif //#if MYSQL_VERSION_ID >= 50600 #endif // #if MYSQL_VERSION_ID >= 50600
#endif #endif

View File

@ -8,8 +8,8 @@
#define MYSQL_DYNAMIC_PLUGIN 1 #define MYSQL_DYNAMIC_PLUGIN 1
#define MYSQL_SERVER 1 #define MYSQL_SERVER 1
//Fix for VIO. We don't want to using method mapping as then a change in the struct will cause the offsets compiled with to // Fix for VIO. We don't want to using method mapping as then a change in the struct will cause the offsets compiled with to
//be wrong. As is the case with ndb which uses a version of Vio with support for ipv6 similar to 5.5 but different from 5.1 // be wrong. As is the case with ndb which uses a version of Vio with support for ipv6 similar to 5.5 but different from 5.1
#define DONT_MAP_VIO #define DONT_MAP_VIO
#include <my_config.h> #include <my_config.h>
@ -19,7 +19,7 @@
#include <mysql_priv.h> #include <mysql_priv.h>
#else #else
//version 5.5.x doesn't contain mysql_priv.h . We need to add the includes provided by it. // version 5.5.x doesn't contain mysql_priv.h . We need to add the includes provided by it.
#if MYSQL_VERSION_ID >= 50505 #if MYSQL_VERSION_ID >= 50505
// These two are not present in 5.7.9 // These two are not present in 5.7.9
@ -43,7 +43,7 @@
#include <sql/sql_table.h> #include <sql/sql_table.h>
#include <sql/sql_view.h> #include <sql/sql_view.h>
//TODO: use mysql mutex instead of pthread // TODO: use mysql mutex instead of pthread
/* /*
#define pthread_mutex_lock mysql_mutex_lock #define pthread_mutex_lock mysql_mutex_lock
#define pthread_mutex_unlock mysql_mutex_unlock #define pthread_mutex_unlock mysql_mutex_unlock
@ -67,8 +67,8 @@
#include <my_dir.h> #include <my_dir.h>
#include <my_sys.h> #include <my_sys.h>
//5.5 use my_free with a single param. 5.1 use with 2 params // 5.5 use my_free with a single param. 5.1 use with 2 params
//based on: http://bazaar.launchpad.net/~mysql/myodbc/5.1/view/head:/util/stringutil.h // based on: http://bazaar.launchpad.net/~mysql/myodbc/5.1/view/head:/util/stringutil.h
#ifndef x_free #ifndef x_free
# if MYSQL_VERSION_ID >= 50500 # if MYSQL_VERSION_ID >= 50500
# define x_free(A) { void *tmp= (A); if (tmp) my_free((char *) tmp); } # define x_free(A) { void *tmp= (A); if (tmp) my_free((char *) tmp); }
@ -77,23 +77,19 @@
# endif # endif
#endif #endif
//MariaDB doesn't have my_getsystime (returns 100 nano seconds) function. They replaced with my_hrtime_t my_hrtime() which returns microseconds // MariaDB doesn't have my_getsystime (returns 100 nano seconds) function. They replaced with my_hrtime_t my_hrtime() which returns microseconds
#if defined(MARIADB_BASE_VERSION) #if defined(MARIADB_BASE_VERSION)
#define my_getsystime() ((my_hrtime()).val * 10) #define my_getsystime() ((my_hrtime()).val * 10)
//MariaDB has a kill service that overrides thd_killed as a macro. It also has thd_killed function defined for backwards compatibility, so we redefine it. // MariaDB has a kill service that overrides thd_killed as a macro. It also has thd_killed function defined for backwards compatibility, so we redefine it.
#undef thd_killed #undef thd_killed
extern "C" int thd_killed(const MYSQL_THD thd); extern "C" int thd_killed(const MYSQL_THD thd);
//MariadDB 10.0.10 removed the include for thd_security_context // MariadDB 10.0.10 removed the include for thd_security_context
#if MYSQL_VERSION_ID >= 100010 #if MYSQL_VERSION_ID >= 100010
extern "C" char *thd_security_context(MYSQL_THD thd, char *buffer, unsigned int length, unsigned int max_query_len); extern "C" char *thd_security_context(MYSQL_THD thd, char *buffer, unsigned int length, unsigned int max_query_len);
#endif #endif
#endif #endif
#endif // MYSQL_INCL_H
#endif //MYSQL_INCL_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,33 +11,33 @@
#define ULONG_PTR uint32_t #define ULONG_PTR uint32_t
#endif #endif
static const char * log_prefix = "Audit Plugin:"; static const char *log_prefix = "Audit Plugin:";
static const unsigned long PAGE_SIZE = GETPAGESIZE() ; static const unsigned long PAGE_SIZE = GETPAGESIZE() ;
//used to indicate how to do the protect/unprotect // used to indicate how to do the protect/unprotect
static bool use_exec_prot = true; static bool use_exec_prot = true;
static int protect(void *addr, size_t len) static int protect(void *addr, size_t len)
{ {
int res = 0; int res = 0;
if(use_exec_prot) if (use_exec_prot)
{ {
res = mprotect(addr,len,PROT_READ|PROT_EXEC); res = mprotect(addr,len,PROT_READ|PROT_EXEC);
} }
else //try doing in a 2 step fashion else // try doing in a 2 step fashion
{ {
mprotect(addr,len,PROT_READ); mprotect(addr,len,PROT_READ);
res = mprotect(addr,len,PROT_READ|PROT_EXEC); res = mprotect(addr,len,PROT_READ|PROT_EXEC);
} }
if(res) if (res)
{ {
sql_print_information( sql_print_information(
"%s unable to protect mode: PROT_READ|PROT_EXEC. Page: %p, Size: %zu, errno: %d, res %d.", "%s unable to protect mode: PROT_READ|PROT_EXEC. Page: %p, Size: %zu, errno: %d, res %d.",
log_prefix, (void *)addr, len, errno, res); log_prefix, (void *)addr, len, errno, res);
//fail only if nx bit is enabled // fail only if nx bit is enabled
FILE * fp = fopen("/proc/cpuinfo", "r"); FILE *fp = fopen("/proc/cpuinfo", "r");
if(NULL == fp) if (NULL == fp)
{ {
sql_print_error( sql_print_error(
"%s unable to verify nx bit. Failed checking /proc/cpuinfo. This may happen if you have SELinux enabled. Disable SELinux execmod protection for mysqld. Page: %p, Size: %zu, errno: %d.", "%s unable to verify nx bit. Failed checking /proc/cpuinfo. This may happen if you have SELinux enabled. Disable SELinux execmod protection for mysqld. Page: %p, Size: %zu, errno: %d.",
@ -45,21 +45,21 @@ static int protect(void *addr, size_t len)
return res; return res;
} }
char buff[1024] = {0}; char buff[1024] = {0};
const char * flags = "flags"; const char *flags = "flags";
bool nxchecked = false; bool nxchecked = false;
while(fgets(buff, 1024, fp) != NULL) while (fgets(buff, 1024, fp) != NULL)
{ {
char * line = buff; char *line = buff;
//trim white space at start // trim white space at start
while ((strlen(line) > 0) && (isspace(line[0]))) while ((strlen(line) > 0) && (isspace(line[0])))
{ {
line++; line++;
} }
if(strncmp(line, flags, strlen(flags)) == 0) if (strncmp(line, flags, strlen(flags)) == 0)
{ {
nxchecked = true; nxchecked = true;
sql_print_information("%s cpuinfo flags line: %s. ",log_prefix, line); sql_print_information("%s cpuinfo flags line: %s. ",log_prefix, line);
if(strstr(line, " nx")) //nx enabled so fail if (strstr(line, " nx")) // nx enabled so fail
{ {
sql_print_error( sql_print_error(
"%s unable to protect page and nx bit enabled. This may happen if you have SELinux enabled. Disable SELinux execmod protection for mysqld. Page: %p, Size: %zu.", "%s unable to protect page and nx bit enabled. This may happen if you have SELinux enabled. Disable SELinux execmod protection for mysqld. Page: %p, Size: %zu.",
@ -71,7 +71,7 @@ static int protect(void *addr, size_t len)
} }
} }
fclose(fp); fclose(fp);
if(!nxchecked) //we didn't find flags string for some reason if (! nxchecked) // we didn't find flags string for some reason
{ {
sql_print_error( sql_print_error(
"%s unable to verify nx bit. Failed finding: %s in /proc/cpuinfo. This may happen if you have SELinux enabled. Disable SELinux execmod protection for mysqld. Page: %p, Size: %zu.", "%s unable to verify nx bit. Failed finding: %s in /proc/cpuinfo. This may happen if you have SELinux enabled. Disable SELinux execmod protection for mysqld. Page: %p, Size: %zu.",
@ -82,66 +82,67 @@ static int protect(void *addr, size_t len)
return 0; return 0;
} }
//will try to unprotect with PROT_READ|PROT_WRITE|PROT_EXEC. If fails (might happen under SELinux) // will try to unprotect with PROT_READ|PROT_WRITE|PROT_EXEC. If fails (might happen under SELinux)
//will use PROT_READ|PROT_WRITE // will use PROT_READ|PROT_WRITE
static int unprotect(void *addr, size_t len) static int unprotect(void *addr, size_t len)
{ {
int res; int res;
if(use_exec_prot) if (use_exec_prot)
{ {
res = mprotect(addr,len,PROT_READ|PROT_WRITE|PROT_EXEC); res = mprotect(addr, len, PROT_READ|PROT_WRITE|PROT_EXEC);
if(res) if (res)
{ {
sql_print_information( sql_print_information(
"%s unable to unprotect. Page: %p, Size: %zu, errno: %d. Using NO EXEC mode.", "%s unable to unprotect. Page: %p, Size: %zu, errno: %d. Using NO EXEC mode.",
log_prefix, (void *)addr, len, errno); log_prefix, (void *)addr, len, errno);
use_exec_prot = false; use_exec_prot = false;
//do a sanity test that we can actually unprotect/protect and that nx bit is off // do a sanity test that we can actually unprotect/protect and that nx bit is off
res = unprotect(addr, len); res = unprotect(addr, len);
if(res) if (res)
{ {
sql_print_error( sql_print_error(
"%s unable to unprotect page. This may happen if you have SELinux enabled. Disable SELinux execmod protection for mysqld. Aborting. Page: %p, Size: %zu, errno: %d.", "%s unable to unprotect page. This may happen if you have SELinux enabled. Disable SELinux execmod protection for mysqld. Aborting. Page: %p, Size: %zu, errno: %d.",
log_prefix, (void *)addr, len, errno); log_prefix, (void *)addr, len, errno);
return res; return res;
} }
res = protect(addr, len); res = protect(addr, len);
sql_print_information("%s protect res: %d", log_prefix, res); sql_print_information("%s protect res: %d", log_prefix, res);
if(res) if (res)
{ {
sql_print_error( sql_print_error(
"%s unable to protect page. This may happen if you have SELinux enabled. Disable SELinux execmod protection for mysqld. Aborting. Page: %p, Size: %zu, errno: %d.", "%s unable to protect page. This may happen if you have SELinux enabled. Disable SELinux execmod protection for mysqld. Aborting. Page: %p, Size: %zu, errno: %d.",
log_prefix, (void *)addr, len, errno); log_prefix, (void *)addr, len, errno);
return res; return res;
} }
} }
else //all is good else // all is good
{ {
return res; return res;
} }
} }
res = mprotect(addr,len,PROT_READ|PROT_WRITE);
if(0 != res) //log the failure res = mprotect(addr, len, PROT_READ|PROT_WRITE);
if (0 != res) // log the failure
{ {
sql_print_error( sql_print_error(
"%s unable to unprotect. Page: %p, Size: %zu, errno: %d. Error.", "%s unable to unprotect. Page: %p, Size: %zu, errno: %d. Error.",
log_prefix, (void *)addr, len, errno); log_prefix, (void *)addr, len, errno);
} }
return res; return res;
} }
//macro to log via sql_print_information only if cond test is enabled // macro to log via sql_print_information only if cond test is enabled
#define cond_info_print(cond_test, ...) do{if(cond_test) sql_print_information(__VA_ARGS__);}while(0) #define cond_info_print(cond_test, ...) do { if (cond_test) sql_print_information(__VA_ARGS__);} while (0)
/* /*
* Get the page address of a given pointer * Get the page address of a given pointer
*/ */
static DATATYPE_ADDRESS get_page_address(void * pointer) static DATATYPE_ADDRESS get_page_address(void *pointer)
{ {
DATATYPE_ADDRESS pageMask = ( ~(PAGE_SIZE - 1) ) ; DATATYPE_ADDRESS pageMask = ( ~(PAGE_SIZE - 1) ) ;
DATATYPE_ADDRESS longp = (unsigned long) pointer; DATATYPE_ADDRESS longp = (unsigned long) pointer;
return (longp & pageMask); return (longp & pageMask);
} }
// //
@ -164,112 +165,115 @@ unsigned int jump_size()
static void WriteJump(void *pAddress, ULONG_PTR JumpTo) static void WriteJump(void *pAddress, ULONG_PTR JumpTo)
{ {
DATATYPE_ADDRESS AddressPage = get_page_address(pAddress); DATATYPE_ADDRESS AddressPage = get_page_address(pAddress);
unprotect((void*)AddressPage, PAGE_SIZE); unprotect((void*)AddressPage, PAGE_SIZE);
BYTE *pCur = (BYTE *) pAddress; BYTE *pCur = (BYTE *) pAddress;
#ifndef __x86_64__ #ifndef __x86_64__
BYTE * pbJmpSrc = pCur + 5; BYTE * pbJmpSrc = pCur + 5;
*pCur++ = 0xE9; // jmp +imm32 *pCur++ = 0xE9; // jmp +imm32
*((ULONG_PTR *)pCur) = JumpTo - (ULONG_PTR)pbJmpSrc; *((ULONG_PTR *)pCur) = JumpTo - (ULONG_PTR)pbJmpSrc;
#else #else
*pCur = 0xff; // jmp [rip+addr] *pCur = 0xff; // jmp [rip+addr]
*(++pCur) = 0x25; *(++pCur) = 0x25;
*((DWORD *) ++pCur) = 0; // addr = 0 *((DWORD *) ++pCur) = 0; // addr = 0
pCur += sizeof (DWORD); pCur += sizeof (DWORD);
*((ULONG_PTR *)pCur) = JumpTo; *((ULONG_PTR *)pCur) = JumpTo;
#endif #endif
//} // DWORD dwBuf = 0; // necessary othewrise the function fails
//DWORD dwBuf = 0; // nessary othewrise the function fails protect((void*)AddressPage, PAGE_SIZE);
protect((void*)AddressPage, PAGE_SIZE);
} }
// //
// Hooks a function // Hooks a function
// //
static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_PTR trampolineFunction, static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_PTR trampolineFunction,
unsigned int *trampolinesize) unsigned int *trampolinesize)
{ {
#define MAX_INSTRUCTIONS 100 #define MAX_INSTRUCTIONS 100
uint8_t raw[MAX_INSTRUCTIONS]; uint8_t raw[MAX_INSTRUCTIONS];
unsigned int uCurrentSize =0; unsigned int uCurrentSize =0;
#ifndef __x86_64__ #ifndef __x86_64__
#define ASM_MODE 32 #define ASM_MODE 32
#else #else
#define ASM_MODE 64 #define ASM_MODE 64
#endif #endif
memcpy (raw,(void*)targetFunction,MAX_INSTRUCTIONS); memcpy(raw, (void*)targetFunction, MAX_INSTRUCTIONS);
ud_t ud_obj; ud_t ud_obj;
ud_init(&ud_obj); ud_init(&ud_obj);
ud_set_input_buffer(&ud_obj, raw, MAX_INSTRUCTIONS); ud_set_input_buffer(&ud_obj, raw, MAX_INSTRUCTIONS);
ud_set_mode(&ud_obj, ASM_MODE); ud_set_mode(&ud_obj, ASM_MODE);
ud_set_syntax(&ud_obj, UD_SYN_INTEL); ud_set_syntax(&ud_obj, UD_SYN_INTEL);
ud_set_pc(&ud_obj, targetFunction); ud_set_pc(&ud_obj, targetFunction);
DWORD InstrSize = 0; DWORD InstrSize = 0;
DATATYPE_ADDRESS trampolineFunctionPage = get_page_address((void*)trampolineFunction); DATATYPE_ADDRESS trampolineFunctionPage = get_page_address((void*)trampolineFunction);
if(unprotect((void*)trampolineFunctionPage, PAGE_SIZE) != 0) if (unprotect((void*)trampolineFunctionPage, PAGE_SIZE) != 0)
{ {
sql_print_error( sql_print_error(
"%s unable to unprotect trampoline function page: %p. Aborting.", "%s unable to unprotect trampoline function page: %p. Aborting.",
log_prefix, (void *)trampolineFunctionPage); log_prefix, (void *)trampolineFunctionPage);
return false; return false;
} }
bool disassemble_valid = false;
while (ud_disassemble(&ud_obj))
{
if(ud_obj.mnemonic == UD_Iinvalid)
{
sql_print_error(
"%s unable to disassemble at address: %p. Aborting.",
log_prefix, (void *)(InstrSize + targetFunction));
break;
}
//make sure there isn't a jmp/call (or similar operand) as these use
//relative addressing and if we copy as is we will mess up the jmp/call target
if(ud_obj.mnemonic == UD_Ijmp || ud_obj.mnemonic == UD_Icall ||
ud_obj.operand[0].type == UD_OP_JIMM)
{
sql_print_error(
"%s unable to disassemble at address: 0x%p. Found relative addressing for instruction: [%s]. Aborting.",
log_prefix, (void *)(InstrSize + targetFunction), ud_insn_asm(&ud_obj));
break;
}
BYTE *pCurInstr = (BYTE *) (InstrSize + (ULONG_PTR) targetFunction); bool disassemble_valid = false;
memcpy((BYTE*)trampolineFunction + uCurrentSize, while (ud_disassemble(&ud_obj))
(void *) pCurInstr, ud_insn_len (&ud_obj)); {
if (ud_obj.mnemonic == UD_Iinvalid)
{
sql_print_error(
"%s unable to disassemble at address: %p. Aborting.",
log_prefix, (void *)(InstrSize + targetFunction));
break;
}
uCurrentSize += ud_insn_len (&ud_obj); // make sure there isn't a jmp/call (or similar operand) as these use
InstrSize += ud_insn_len (&ud_obj); // relative addressing and if we copy as is we will mess up the jmp/call target
if (InstrSize >= jump_size()) //we have enough space so break if (ud_obj.mnemonic == UD_Ijmp || ud_obj.mnemonic == UD_Icall ||
{ ud_obj.operand[0].type == UD_OP_JIMM)
disassemble_valid = true; {
break; sql_print_error(
} "%s unable to disassemble at address: 0x%p. Found relative addressing for instruction: [%s]. Aborting.",
} log_prefix, (void *)(InstrSize + targetFunction), ud_insn_asm(&ud_obj));
if(protect((void*)trampolineFunctionPage, PAGE_SIZE)) //0 valid return break;
}
BYTE *pCurInstr = (BYTE *) (InstrSize + (ULONG_PTR) targetFunction);
memcpy((BYTE*)trampolineFunction + uCurrentSize,
(void *) pCurInstr, ud_insn_len (&ud_obj));
uCurrentSize += ud_insn_len (&ud_obj);
InstrSize += ud_insn_len (&ud_obj);
if (InstrSize >= jump_size()) // we have enough space so break
{
disassemble_valid = true;
break;
}
}
if (protect((void*)trampolineFunctionPage, PAGE_SIZE)) // 0 valid return
{ {
sql_print_error( sql_print_error(
"%s unable to protect page. Error. Page: %p.", "%s unable to protect page. Error. Page: %p.",
log_prefix, (void *)trampolineFunctionPage); log_prefix, (void *)trampolineFunctionPage);
return false; return false;
} }
if(!disassemble_valid) //something went wrong. log was written before so return false
{ if (! disassemble_valid) // something went wrong. log was written before so return false
return false; {
} return false;
WriteJump( (BYTE*)trampolineFunction + uCurrentSize, targetFunction + InstrSize); }
WriteJump((void *) targetFunction, newFunction);
*trampolinesize = uCurrentSize; WriteJump((BYTE*)trampolineFunction + uCurrentSize, targetFunction + InstrSize);
return true; WriteJump((void *) targetFunction, newFunction);
*trampolinesize = uCurrentSize;
return true;
} }
// //
@ -277,18 +281,18 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG
// //
static void UnhookFunction(ULONG_PTR Function,ULONG_PTR trampolineFunction , unsigned int trampolinesize) static void UnhookFunction(ULONG_PTR Function, ULONG_PTR trampolineFunction, unsigned int trampolinesize)
{ {
DATATYPE_ADDRESS FunctionPage = get_page_address((void*)Function); DATATYPE_ADDRESS FunctionPage = get_page_address((void*)Function);
if(unprotect((void*)FunctionPage, PAGE_SIZE) != 0) if (unprotect((void*)FunctionPage, PAGE_SIZE) != 0)
{ {
sql_print_error( sql_print_error(
"%s Unhook not able to unprotect function page: %p. Aborting.", "%s Unhook not able to unprotect function page: %p. Aborting.",
log_prefix, (void * )FunctionPage); log_prefix, (void * )FunctionPage);
return; return;
} }
memcpy((void *) Function, (void*)trampolineFunction,trampolinesize); memcpy((void *) Function, (void*)trampolineFunction,trampolinesize);
protect((void*)FunctionPage, PAGE_SIZE); protect((void*)FunctionPage, PAGE_SIZE);
} }
/** /**
@ -306,21 +310,21 @@ static void UnhookFunction(ULONG_PTR Function,ULONG_PTR trampolineFunction , uns
* which contains a bunch of nops. * which contains a bunch of nops.
* @param info_print if true will print info as progressing * @param info_print if true will print info as progressing
* @Return 0 on success otherwise failure * @Return 0 on success otherwise failure
* @See MS Detours paper: http://research.microsoft.com/pubs/68568/huntusenixnt99.pdf for some background info. * @See MS Detours paper: http:// research.microsoft.com/pubs/68568/huntusenixnt99.pdf for some background info.
*/ */
int hot_patch_function (void* targetFunction, void* newFunction, void * trampolineFunction, unsigned int *trampolinesize, bool info_print) int hot_patch_function(void *targetFunction, void *newFunction, void *trampolineFunction, unsigned int *trampolinesize, bool info_print)
{ {
DATATYPE_ADDRESS trampolinePage = get_page_address(trampolineFunction); DATATYPE_ADDRESS trampolinePage = get_page_address(trampolineFunction);
cond_info_print(info_print, "%s hot patching function: %p, trampolineFunction: %p trampolinePage: %p",log_prefix, (void *)targetFunction, (void *)trampolineFunction, (void *)trampolinePage); cond_info_print(info_print, "%s hot patching function: %p, trampolineFunction: %p trampolinePage: %p",log_prefix, (void *)targetFunction, (void *)trampolineFunction, (void *)trampolinePage);
if (HookFunction((ULONG_PTR) targetFunction, (ULONG_PTR) newFunction, if (HookFunction((ULONG_PTR) targetFunction, (ULONG_PTR) newFunction,
(ULONG_PTR) trampolineFunction, trampolinesize)) (ULONG_PTR) trampolineFunction, trampolinesize))
{ {
return 0; return 0;
} }
else else
{ {
return -1; return -1;
} }
} }
@ -332,11 +336,11 @@ int hot_patch_function (void* targetFunction, void* newFunction, void * trampoli
* @param trampolineFunction a function which contains a jump back to the targetFunction. * @param trampolineFunction a function which contains a jump back to the targetFunction.
* @param log_file if not null will log about progress of installing the plugin * @param log_file if not null will log about progress of installing the plugin
*/ */
void remove_hot_patch_function (void* targetFunction, void * trampolineFunction, unsigned int trampolinesize, bool info_print) void remove_hot_patch_function(void *targetFunction, void *trampolineFunction, unsigned int trampolinesize, bool info_print)
{ {
if(trampolinesize == 0) if (trampolinesize == 0)
{ {
//nothing todo. As hot patch was not set. // nothing todo. As hot patch was not set.
return; return;
} }
DATATYPE_ADDRESS targetPage = get_page_address(targetFunction); DATATYPE_ADDRESS targetPage = get_page_address(targetFunction);

0
src/md5.cc Executable file → Normal file
View File