37 Commits

Author SHA1 Message Date
Guy Lichtman
a935dc105a offsets upto: 5.5.38 and 5.6.19 including mariadb suppoort
cleanup for global params which are set with an update function. For these params we use a static buffer instead of relying on memalloc as mem alloc works differently between versions.
SELinux: More checks to make sure that when selinux is enabled we fail installation.
2014-06-10 11:04:10 +03:00
Guy Lichtman
a6d41da62e better support for mariadb (including offsets for 5.5.32-36). Added offsets for 5.5.37 and 5.6.17. 2014-04-17 11:52:47 +03:00
Guy Lichtman
f2153dbeee Support to compile with MariaDB src code (improved comment). 2014-02-26 15:41:49 +02:00
Guy Lichtman
5a63e7c0a1 Support to compile with MariaDB src code. issue #73. 2014-02-13 00:12:47 +02:00
Guy Lichtman
95a8bec46a fix for issue #72 . treat failure to hot patch log_slow_statement as non critical 2014-01-29 17:54:54 +02:00
Guy Lichtman
694d901a45 comment about interface change 2014-01-29 17:53:16 +02:00
Guy Lichtman
73876cf8f9 compilation fix for 5.5.34 and 5.6.14 (and above). issue #66. 2014-01-09 19:23:54 +02:00
Guy Lichtman
9e2dab5305 support for 5.6.14 and up where audit interface version changed 2013-12-25 11:59:49 +02:00
Guy Lichtman
9847e647ff fixing compilation issue with newer mysql code base. issue 66. 2013-12-14 22:41:54 +02:00
Guy Lichtman
a1baf12ebd offsets for 5.1.73, 5.5.35, 5.6.15 2013-12-14 22:27:06 +02:00
Guy Lichtman
0ec8a08312 issue #28: support for empty user whitelisting using "{}".
fix to log prepare statements.
2013-11-09 21:46:48 +02:00
Guy Lichtman
4b680c3f35 Support for 5.1.72, 5.5.34 and 5.6.14. Added new offsets for Security_context stuff. General fix for 5.6.12 view handling. Issue #62. 2013-10-11 16:55:33 +02:00
Guy Lichtman
5f6bff811e remove unneeded comma at end of offset list 2013-08-04 11:25:23 +03:00
Guy Lichtman
f9da2080a5 Merge pull request #57 from conmame/add_mysql5.6_build
Add MySQL5.6 build comment
2013-08-04 00:54:35 -07:00
Guy Lichtman
f822890f45 Merge pull request #56 from conmame/add_mysql5.6_offset
Add MySQL5.6.13 offsets
2013-08-04 00:53:48 -07:00
Yutaka Hoshino
e0e9529802 Add MySQL5.6 build comment 2013-07-31 12:52:57 +09:00
Yutaka Hoshino
0b6223ea50 Add MySQL5.6.13 offsets 2013-07-31 12:49:41 +09:00
Guy Lichtman
78abdeab07 issue #55. Change case to avoid having audit plugin status vars registered first. If a plugin registers first its status var, a "show status" after an uninstall of the plugin will cause a crash. 2013-07-06 01:39:39 +03:00
Guy Lichtman
72058ca73a offsets for 5.1.70 5.1.32 and 5.6.12 2013-07-01 17:57:08 +03:00
Guy Lichtman
66bf627141 Merge pull request #53 from conmame/mysql5.6.12_offsets
Add MySQL5.6.12 rpm offsets
2013-06-18 08:00:15 -07:00
Guy Lichtman
892aa00e24 Merge pull request #54 from conmame/fix_compiling_step
Add execute bootstrap.sh step in MySQL5.5 section
2013-06-18 04:08:34 -07:00
Yutaka Hoshino
2e137eec8c Add execute bootstrap.sh step in MySQL5.5 section 2013-06-18 17:13:32 +09:00
Yutaka Hoshino
59a4dae9f7 Add MySQL5.6.12 rpm offsets 2013-06-17 17:27:08 +09:00
Guy Lichtman
cb0b06e329 offsets for: Percona-Server-server-55-5.5.22-rel25.2.237.rhel5.x86_64
related to issue #49
2013-06-16 10:36:50 +03:00
Guy Lichtman
a7d789d371 Suport 5.6.x
Offsets upto 5.6.11, 5.5.31, 5.1.69
Use -Wall for compilation and fix warnings.
fix for issue #40
fix for issue #33
2013-06-06 21:26:39 +03:00
Guy Lichtman
cf395c3f66 issue #30. Change uninstall plugin logic. First time uninstall will
just cause the audit plugin to stop working and there is need to
do an additional install to actually unload the shared library.
2013-03-06 19:00:51 +02:00
Guy Lichtman
2d9337d296 issue #35. Wimplicit only with C and not C++ to support newer versions of gcc. 2013-03-06 18:59:16 +02:00
Guy Lichtman
b24e1913fa offsets for 5.1.66-67, 5.5.29-30 2013-03-01 16:57:34 +02:00
Guy Lichtman
bc92825963 new object iteration logic including fix for crash reported in issue #31 2013-02-22 16:58:28 +02:00
Guy Lichtman
f717564993 Fix for issue #32. Check also object from query cache when checking audit_record_objs. 2013-02-06 17:19:39 +02:00
Guy Lichtman
8d85b09b72 Treat Quit command as an empty object set. Issue #26. 2013-01-12 18:04:38 +02:00
Guy Lichtman
af0a366267 safe guards regarding checking for record objs. issue #29. 2013-01-12 10:53:48 +02:00
Guy Lichtman
e4871f5305 fix typo in freeing whitelist users string. issue #27 2013-01-09 22:10:43 +02:00
Guy Lichtman
c53dab30e3 SELinux support. Added check to fail plugin installation if page mprotect doesn't work as needed and nx bit is turned on. Happens when SELinux is enabled. issue #24 2013-01-09 21:41:45 +02:00
Guy Lichtman
a3df5d6454 Small modifications to user white list feature. 2012-12-24 10:57:16 +02:00
Guy Lichtman
4cfb7a5240 Merge pull request #23 from akashsinha/userFilter
Option added to skip queries of whitelisted users. Doing full merge and then will edit some changes.
2012-12-23 09:02:32 -08:00
Akash Sinha
5cdc6dd456 Option added to skip queries of whitelisted users
Ex: audit_whitelist_users=john,doe
2012-12-20 19:31:44 +05:30
11 changed files with 1492 additions and 417 deletions

14
compiling.txt Normal file → Executable file
View File

@@ -35,19 +35,25 @@ This will create configure script. Then run:
CXX='gcc -static-libgcc' CC='gcc -static-libgcc' ./configure --with-mysql=mysql-5.1.40
==== MySQL 5.5 ======
==== MySQL 5.5 / 5.6 ======
Extract MySQL 5.5 source code
Extract MySQL 5.5 or 5.6 source code
go to mysql-src dir and run:
cd mysql-5.5.8
cd mysql-5.5.x or mysql-5.6.x
cmake .
make
Note: to speed things up it is possible to build just the following directories:
libservices
extra
back to working dir and run:
CXX='gcc -static-libgcc' CC='gcc -static-libgcc' ./configure --with-mysql=mysql-5.5.8 --with-mysql-libservices=mysql-5.5.8/libservices/libmysqlservices.a
chmod +x bootstrap.sh
./bootstrap.sh
CXX='gcc -static-libgcc' CC='gcc -static-libgcc' ./configure --with-mysql=mysql-5.x.x --with-mysql-libservices=mysql-5.x.x/libservices/libmysqlservices.a
make
==== Compiling with make =====

5
configure.ac Normal file → Executable file
View File

@@ -88,9 +88,10 @@ AC_PATH_PROG(DIFF, diff, diff)
#CPPFLAGS="$CPPFLAGS -DMYSQL_DYNAMIC_PLUGIN -DMYSQL_SERVER"
#we can add the following flags for better error catching: -Werror -Wimplicit
CPPFLAGS="$CPPFLAGS -Werror -Wimplicit"
CPPFLAGS="$CPPFLAGS -Werror -Wall"
CFLAGS="$CFLAGS -Wimplicit"
# From MySQL: Disable exceptions as they seams to create problems with gcc and threads.
CXXFLAGS="-fno-implicit-templates -fno-exceptions -fno-rtti "
CXXFLAGS="-fno-implicit-templates -fno-exceptions -fno-rtti -Wno-reorder -Wno-strict-aliasing"
#add pthread libs
LIBS="$LIBS -lpthread"

141
include/audit_handler.h Executable file → Normal file
View File

@@ -30,13 +30,16 @@ typedef struct _THDPRINTED {
} THDPRINTED;
#define MAX_COMMAND_CHAR_NUMBERS 40
const char * retrieve_command (THD * thd);
const char * retrieve_command (THD * thd, bool & is_sql_cmd);
typedef size_t OFFSET;
#define MAX_COM_STATUS_VARS_RECORDS 512
#define MAX_OBJECT_CHAR_NUMBERS 130
//mysql max identifier is 64 so 2*64 + . and null
#define MAX_OBJECT_CHAR_NUMBERS 131
#define MAX_USER_CHAR_NUMBERS 20
#define MAX_NUM_OBJECT_ELEM 256
#define MAX_NUM_USER_ELEM 256
/**
* The struct usd to hold offsets. We should have one per version.
@@ -51,6 +54,10 @@ typedef struct ThdOffsets
OFFSET command;
OFFSET lex;
OFFSET lex_comment;
OFFSET sec_ctx_user;
OFFSET sec_ctx_host;
OFFSET sec_ctx_ip;
OFFSET sec_ctx_priv_user;
} ThdOffsets;
/*
@@ -75,12 +82,36 @@ public:
class ThdSesData {
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);
private:
THD *m_pThd;
const char *m_CmdName;
const char *m_UserName;
bool m_isSqlCmd;
enum ObjectIterType m_objIterType;
//pointer for iterating tables
TABLE_LIST * m_tables;
//indicator if we are at the first table
bool m_firstTable;
//used for query cache iter
QueryTableInf * m_tableInf;
int m_index;
protected:
ThdSesData (const ThdSesData& );
ThdSesData &operator =(const ThdSesData& );
@@ -117,7 +148,7 @@ public:
*/
virtual ssize_t stop_msg_format(IWriter * writer) { return 0; }
static const char * retrive_object_type (TABLE_LIST *pObj);
static const char * retrieve_object_type (TABLE_LIST *pObj);
static QueryTableInf* getQueryCacheTableList1 (THD *thd);
//utility functions for fetching thd stuff
static inline my_thread_id thd_inst_thread_id(THD * thd)
@@ -135,6 +166,69 @@ public:
return (Security_context *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.main_security_ctx);
}
static inline const char * thd_inst_main_security_ctx_user(THD * thd)
{
Security_context * sctx = thd_inst_main_security_ctx(thd);
if(!Audit_formatter::thd_offsets.sec_ctx_user) //no offsets use compiled in header
{
return sctx->user;
}
return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_user);
}
static inline const char * thd_inst_main_security_ctx_host(THD * 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
{
//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
#if ( !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50534 && MYSQL_VERSION_ID < 50600) || (MYSQL_VERSION_ID >= 50614)
return sctx->get_host()->ptr();
#else
return sctx->host;
#endif
}
return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_host);
}
static inline const char * thd_inst_main_security_ctx_ip(THD * thd)
{
Security_context * sctx = thd_inst_main_security_ctx(thd);
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()
#if ( !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50534 && MYSQL_VERSION_ID < 50600) || (MYSQL_VERSION_ID >= 50614)
return sctx->get_ip()->ptr();
#else
return sctx->ip;
#endif
}
return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_ip);
}
static inline const char * thd_inst_main_security_ctx_priv_user(THD * 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
{
return sctx->priv_user;
}
#if MYSQL_VERSION_ID < 50505
//in 5.1.x priv_user is a pointer
return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_priv_user);
#else
//in 5.5 and up priv_user is an array (char priv_user[USERNAME_LENGTH])
return (const char *) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_priv_user);
#endif
}
static inline int thd_inst_command(THD * thd)
{
return *(int *) (((unsigned char *) thd) + Audit_formatter::thd_offsets.command);
@@ -144,6 +238,22 @@ public:
{
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
static inline const char * table_get_db_name(TABLE_LIST * table)
{
return table->db;
}
static inline const char * table_get_name(TABLE_LIST * table)
{
return table->table_name;
}
static inline bool table_is_view(TABLE_LIST * table)
{
return table->view_tables != 0;
}
};
@@ -180,8 +290,8 @@ public:
protected:
Audit_json_formatter& operator =(const Audit_json_formatter& b) {};
Audit_json_formatter(const Audit_json_formatter& ) {};
Audit_json_formatter& operator =(const Audit_json_formatter& b);
Audit_json_formatter(const Audit_json_formatter& );
};
@@ -291,8 +401,9 @@ protected:
{
rw_unlock(&LOCK_audit);
}
Audit_handler & operator=(const Audit_handler&) {};
Audit_handler(const Audit_handler&) {};
//override default assignment and copy to protect against creating additional instances
Audit_handler & operator=(const Audit_handler&);
Audit_handler(const Audit_handler&);
private:
//bool indicating if to print offset errors to log or not
bool m_print_offset_err;
@@ -326,7 +437,7 @@ class Audit_file_handler: public Audit_io_handler
public:
Audit_file_handler() :
m_log_file(NULL), m_sync_period(0), m_sync_counter(0), m_filename(NULL)
m_sync_period(0), m_filename(NULL), m_log_file(NULL), m_sync_counter(0)
{
}
@@ -352,8 +463,9 @@ public:
ssize_t write(const char * data, size_t size);
//static void print_sleep (THD *thd, int delay_ms);
protected:
Audit_file_handler & operator=(const Audit_file_handler&) {};
Audit_file_handler(const Audit_file_handler&) {};
//override default assignment and copy to protect against creating additional instances
Audit_file_handler & operator=(const Audit_file_handler&);
Audit_file_handler(const Audit_file_handler&);
virtual void handler_start();
virtual void handler_stop();
@@ -404,8 +516,9 @@ public:
ssize_t write(const char * data, size_t size);
protected:
Audit_socket_handler & operator=(const Audit_socket_handler&) {};
Audit_socket_handler(const Audit_socket_handler&) {};
//override default assignment and copy to protect against creating additional instances
Audit_socket_handler & operator=(const Audit_socket_handler&);
Audit_socket_handler(const Audit_socket_handler&);
virtual void handler_start();
virtual void handler_stop();
@@ -420,7 +533,7 @@ protected:
{
if (m_vio)
{
vio_close((Vio*)m_vio);
//no need for vio_close as is called by delete (additionally close changed its name to vio_shutdown in 5.6.11)
vio_delete((Vio*)m_vio);
}
m_vio = NULL;
@@ -428,5 +541,5 @@ protected:
};
#endif /* AUDIT_HANDLER_H_ */

55
include/md5.h Executable file
View File

@@ -0,0 +1,55 @@
/*
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
* MD5 Message-Digest Algorithm (RFC 1321).
*
* Homepage:
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
*
* Author:
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
*
* This software was written by Alexander Peslyak in 2001. No copyright is
* claimed, and the software is hereby placed in the public domain.
* In case this attempt to disclaim copyright and place the software in the
* public domain is deemed null and void, then the software is
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See md5.c for more information.
*/
#if !defined(_MD5_H)
#define _MD5_H
#include "mysql_inc.h"
#if MYSQL_VERSION_ID >= 50600
/* Any 32-bit or wider unsigned integer data type will do */
typedef unsigned int MD5_u32plus;
typedef struct {
MD5_u32plus lo, hi;
MD5_u32plus a, b, c, d;
unsigned char buffer[64];
MD5_u32plus block[16];
} MD5_CTX;
extern void MD5_Init(MD5_CTX *ctx);
extern void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size);
extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
//define the my_MD5* functions
#define my_MD5Context MD5_CTX
#define my_MD5Init MD5_Init
#define my_MD5Update MD5_Update
#define my_MD5Final MD5_Final
#endif //#if MYSQL_VERSION_ID >= 50600
#endif

14
include/mysql_inc.h Executable file → Normal file
View File

@@ -5,7 +5,7 @@
#define HAVE_CONFIG_H
#endif
#define MYSQL_DYNAMIC_PLUGIN
#define MYSQL_DYNAMIC_PLUGIN 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
@@ -21,6 +21,10 @@
#include <my_pthread.h>
#include <sql_priv.h>
#include <mysql/plugin.h>
#if MYSQL_VERSION_ID >= 50600
//in 5.6 we use the audit plugin interface
#include <mysql/plugin_audit.h>
#endif
#include <sql_parse.h>
#include <sql_class.h>
#include <my_global.h>
@@ -58,6 +62,14 @@
# endif
#endif
//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)
#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.
#undef thd_killed
extern "C" int thd_killed(const MYSQL_THD thd);
#endif
#endif //MYSQL_INCL_H

10
offset-extract/offset-extract.sh Normal file → Executable file
View File

@@ -22,8 +22,16 @@ if [ $? = 0 ]; then
MYVER=`echo "$MYVER" | grep -P -o '.+(?=-log)'`
fi
COMMAND_MEMBER=command
#in 5.6 command member is named m_command
echo $MYVER | grep -P '^5.6' > /dev/null
if [ $? = 0 ]; then
COMMAND_MEMBER=m_command
fi
echo "set logging on" > offsets.gdb
echo 'printf "{\"'$MYVER'\",\"'$MYMD5'\", %d, %d, %d, %d, %d, %d}", ((size_t)&((THD *)log_slow_statement)->query_id) - (size_t)log_slow_statement, ((size_t)&((THD *)log_slow_statement)->thread_id) - (size_t)log_slow_statement, ((size_t)&((THD *)log_slow_statement)->main_security_ctx) - (size_t)log_slow_statement, ((size_t)&((THD *)log_slow_statement)->command) - (size_t)log_slow_statement, ((size_t)&((THD *)log_slow_statement)->lex) - (size_t)log_slow_statement, (size_t)&((LEX*)log_slow_statement)->comment - (size_t) log_slow_statement' >> offsets.gdb
echo 'printf "{\"'$MYVER'\",\"'$MYMD5'\", %d, %d, %d, %d, %d, %d, %d, %d, %d, %d}", ((size_t)&((THD *)log_slow_statement)->query_id) - (size_t)log_slow_statement, ((size_t)&((THD *)log_slow_statement)->thread_id) - (size_t)log_slow_statement, ((size_t)&((THD *)log_slow_statement)->main_security_ctx) - (size_t)log_slow_statement, ((size_t)&((THD *)log_slow_statement)->'$COMMAND_MEMBER') - (size_t)log_slow_statement, ((size_t)&((THD *)log_slow_statement)->lex) - (size_t)log_slow_statement, (size_t)&((LEX*)log_slow_statement)->comment - (size_t) log_slow_statement, ((size_t)&((Security_context *)log_slow_statement)->user) - (size_t)log_slow_statement, ((size_t)&((Security_context *)log_slow_statement)->host) - (size_t)log_slow_statement, ((size_t)&((Security_context *)log_slow_statement)->ip) - (size_t)log_slow_statement, ((size_t)&((Security_context *)log_slow_statement)->priv_user) - (size_t)log_slow_statement' >> offsets.gdb
SYMPARAM=""
if [ -n "$2" ]; then

2
src/Makefile.am Normal file → Executable file
View File

@@ -20,7 +20,7 @@ pkgplugindir = $(MYSQL_PLUGIN_DIR)
pkgplugin_LTLIBRARIES = libaudit_plugin.la
libaudit_plugin_la_LDFLAGS = -module -Wl,--version-script=MySQLPlugin.map
libaudit_plugin_la_SOURCES = hot_patch.cc audit_plugin.cc audit_handler.cc
libaudit_plugin_la_SOURCES = hot_patch.cc audit_plugin.cc audit_handler.cc md5.cc
libaudit_plugin_la_LIBADD = $(top_srcdir)/yajl/src/libyajl.la $(top_srcdir)/udis86/libudis86/libudis86.la $(MYSQL_LIBSERVICES)

View File

@@ -23,28 +23,6 @@
#include <sys/un.h>
#include "static_assert.h"
/**
* Will write into buff the date prefix for txt formatter. Return the number of bytes written
* (not including null terminate).
*/
static int log_date_prefix(char * buff, size_t buff_size)
{
struct tm tm_tmp;
time_t result= time(NULL);
localtime_r(&result, &tm_tmp);
//my_snprintf is limited regarding formatting but sufficient for this
return my_snprintf(buff, buff_size, "%02d%02d%02d %2d:%02d:%02d: ",
tm_tmp.tm_year % 100,
tm_tmp.tm_mon+1,
tm_tmp.tm_mday,
tm_tmp.tm_hour,
tm_tmp.tm_min,
tm_tmp.tm_sec);
}
//utility macro to log also with a date as a prefix
#define log_with_date(f, ...) do{\
struct tm tm_tmp;\
@@ -69,9 +47,9 @@ const char * Audit_json_formatter::DEF_MSG_DELIMITER = "\\n";
#define C_STRING_WITH_LEN(X) ((char *) (X)), ((size_t) (sizeof(X) - 1))
const char * Audit_formatter::retrive_object_type (TABLE_LIST *pObj)
const char * Audit_formatter::retrieve_object_type (TABLE_LIST *pObj)
{
if (pObj->view)
if (table_is_view(pObj))
{
return "VIEW";
}
@@ -81,7 +59,7 @@ const char * Audit_formatter::retrive_object_type (TABLE_LIST *pObj)
void Audit_handler::stop_all()
{
for (int i = 0; i < MAX_AUDIT_HANDLERS_NUM; ++i)
for (size_t i = 0; i < MAX_AUDIT_HANDLERS_NUM; ++i)
{
if (m_audit_handler_list[i] != NULL)
{
@@ -92,7 +70,7 @@ void Audit_handler::stop_all()
void Audit_handler::log_audit_all(ThdSesData *pThdData)
{
for (int i = 0; i < MAX_AUDIT_HANDLERS_NUM; ++i)
for (size_t i = 0; i < MAX_AUDIT_HANDLERS_NUM; ++i)
{
if (m_audit_handler_list[i] != NULL)
{
@@ -259,8 +237,14 @@ void Audit_socket_handler::handler_start()
struct sockaddr_un UNIXaddr;
UNIXaddr.sun_family = AF_UNIX;
strmake(UNIXaddr.sun_path, m_sockname, sizeof(UNIXaddr.sun_path)-1);
#if MYSQL_VERSION_ID < 50600
if (my_connect(sock,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr),
m_connect_timeout))
#else
//in 5.6 timeout is in ms
if (vio_socket_connect((Vio*)m_vio,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr),
m_connect_timeout * 1000))
#endif
{
sql_print_error(
"%s unable to connect to socket: %s. err: %s. audit socket handler disabled!!",
@@ -343,6 +327,23 @@ static inline void yajl_add_obj( yajl_gen gen, const char *db,const char* ptype
yajl_add_string_val(gen, "obj_type",ptype);
}
static inline const char * retrieve_user (THD * thd)
{
const char * user = Audit_formatter::thd_inst_main_security_ctx_user(thd);
if(user != NULL && *user != 0x0) //non empty
{
return user;
}
user = Audit_formatter::thd_inst_main_security_ctx_priv_user(thd); //try using priv user
if(user != NULL && *user != 0x0) //non empty
{
return user;
}
return ""; //always use at least the empty string
}
//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
@@ -391,12 +392,9 @@ static const char * thd_query_str(THD * thd, size_t * len)
ssize_t Audit_json_formatter::event_format(ThdSesData* pThdData, IWriter * writer)
{
unsigned long thdid = thd_get_thread_id(pThdData->getTHD());
query_id_t qid = thd_inst_query_id(pThdData->getTHD());
int command = thd_inst_command(pThdData->getTHD());
Security_context * sctx = thd_inst_main_security_ctx(pThdData->getTHD());
THD * thd = pThdData->getTHD();
unsigned long thdid = thd_get_thread_id(thd);
query_id_t qid = thd_inst_query_id(thd);
//initialize yajl
yajl_gen gen = yajl_gen_alloc(&config, NULL);
@@ -409,80 +407,39 @@ ssize_t Audit_json_formatter::event_format(ThdSesData* pThdData, IWriter * write
yajl_add_uint64(gen, "date", ts);
yajl_add_uint64(gen, "thread-id", thdid);
yajl_add_uint64(gen, "query-id", qid);
yajl_add_string_val(gen, "user", sctx->user);
yajl_add_string_val(gen, "priv_user", sctx->priv_user);
yajl_add_string_val(gen, "host", sctx->host);
yajl_add_string_val(gen, "ip", sctx->ip);
yajl_add_string_val(gen, "user", pThdData->getUserName());
yajl_add_string_val(gen, "priv_user", Audit_formatter::thd_inst_main_security_ctx_priv_user(thd));
yajl_add_string_val(gen, "host", Audit_formatter::thd_inst_main_security_ctx_host(thd));
yajl_add_string_val(gen, "ip", Audit_formatter::thd_inst_main_security_ctx_ip(thd));
const char *cmd = pThdData->getCmdName();
//only print tables if lex is not null and it is not a quit command
LEX * pLex = Audit_formatter::thd_lex(pThdData->getTHD());
QueryTableInf *pQuery_cache_table_list = getQueryCacheTableList1 (pThdData->getTHD());
if (pLex && command != COM_QUIT && pLex->query_tables == NULL && pQuery_cache_table_list)
{
yajl_add_string_val(gen, "cmd", "select");
yajl_add_string(gen, "objects");
yajl_gen_array_open(gen);
for (int i=0;i<pQuery_cache_table_list->num_of_elem && i < MAX_NUM_QUERY_TABLE_ELEM && pQuery_cache_table_list->num_of_elem >=0;i++)
{
yajl_gen_map_open(gen);
yajl_add_obj (gen, pQuery_cache_table_list->db[i],pQuery_cache_table_list->object_type[i],pQuery_cache_table_list->table_name[i] );
yajl_gen_map_close(gen);
}
yajl_gen_array_close(gen);
}
else
{
yajl_add_string_val(gen, "cmd", cmd);
}
if (strcmp (cmd,"Init DB") ==0 || strcmp (cmd, "SHOW TABLES")== 0 || strcmp (cmd, "SHOW TABLE")==0)
{
if ((pThdData->getTHD())->db !=0)
{
yajl_add_string(gen, "objects");
yajl_gen_array_open(gen);
yajl_add_obj (gen,(pThdData->getTHD())->db,"database", NULL);
yajl_gen_array_close(gen);
}
}
if (pLex && command != COM_QUIT && pLex->query_tables)
yajl_add_string_val(gen, "cmd", cmd);
//get objects
if(pThdData->startGetObjects())
{
yajl_add_string(gen, "objects");
yajl_gen_array_open(gen);
TABLE_LIST * table = pLex->query_tables;
bool isFirstElementInView = true;
while (table)
const char * db_name = NULL;
const char * obj_name = NULL;
const char * obj_type = NULL;
while(pThdData->getNextObject(&db_name, &obj_name, &obj_type))
{
yajl_gen_map_open(gen);
if (isFirstElementInView && strstr (cmd,"_view")!=NULL )
{
yajl_add_obj (gen,table->get_db_name(), "view",table->get_table_name());
isFirstElementInView = false;
}
else
{
yajl_add_obj (gen,table->get_db_name(), retrive_object_type(table),table->get_table_name());
}
yajl_add_obj (gen, db_name, obj_type, obj_name );
yajl_gen_map_close(gen);
table = table->next_global;
}
yajl_gen_array_close(gen);
}
size_t qlen = 0;
const char * query = thd_query_str(pThdData->getTHD(), &qlen);
if (query && qlen > 0)
{
CHARSET_INFO *col_connection = Item::default_charset();
#if MYSQL_VERSION_ID < 50600
CHARSET_INFO *col_connection;
#else
const CHARSET_INFO *col_connection;
#endif
col_connection = Item::default_charset();
if (strcmp (col_connection->csname,"utf8")!=0) {
String sQuery (query,col_connection) ;
pThdData->getTHD()->convert_string (&sQuery,col_connection,&my_charset_utf8_general_ci);
@@ -527,8 +484,116 @@ ssize_t Audit_json_formatter::event_format(ThdSesData* pThdData, IWriter * write
ThdSesData::ThdSesData (THD *pTHD) : m_pThd (pTHD), m_CmdName(NULL)
ThdSesData::ThdSesData (THD *pTHD) :
m_pThd (pTHD), m_CmdName(NULL), m_UserName(NULL),
m_objIterType(OBJ_NONE), m_tables(NULL), m_firstTable(true),
m_tableInf(NULL), m_index(0), m_isSqlCmd(false)
{
m_CmdName = retrieve_command (m_pThd);
m_CmdName = retrieve_command (m_pThd, m_isSqlCmd);
m_UserName = retrieve_user (m_pThd);
}
bool ThdSesData::startGetObjects()
{
//reset vars as this may be called multiple times
m_objIterType = OBJ_NONE;
m_tables = NULL;
m_firstTable = true;
m_index = 0;
m_tableInf = Audit_formatter::getQueryCacheTableList1(getTHD());
int command = Audit_formatter::thd_inst_command(getTHD());
LEX * pLex = Audit_formatter::thd_lex(getTHD());
//query cache case
if(pLex && command == COM_QUERY && m_tableInf && m_tableInf->num_of_elem > 0)
{
m_objIterType = OBJ_QUERY_CACHE;
return true;
}
const char *cmd = getCmdName();
//commands which have single database object
if (strcmp (cmd,"Init DB") ==0 || strcmp (cmd, "SHOW TABLES")== 0 || strcmp (cmd, "SHOW TABLE")==0)
{
if(getTHD()->db)
{
m_objIterType = OBJ_DB;
return true;
}
return false;
}
//only return query tabls if command is COM_QUERY
//TODO: check if other commands can also generate query tables such as "show fields"
if (pLex && command == COM_QUERY && pLex->query_tables)
{
m_tables = pLex->query_tables;
m_objIterType = OBJ_TABLE_LIST;
return true;
}
//no objects
return false;
}
bool ThdSesData::getNextObject(const char ** db_name, const char ** obj_name, const char ** obj_type)
{
switch(m_objIterType)
{
case OBJ_DB:
{
if(m_firstTable)
{
*db_name = getTHD()->db;
*obj_name = NULL;
if(obj_type)
{
*obj_type = "DATABASE";
}
m_firstTable = false;
return true;
}
return false;
}
case OBJ_QUERY_CACHE:
{
if(m_index < m_tableInf->num_of_elem &&
m_index< MAX_NUM_QUERY_TABLE_ELEM)
{
*db_name = m_tableInf->db[m_index];
*obj_name = m_tableInf->table_name[m_index];
if(obj_type)
{
*obj_type = m_tableInf->object_type[m_index];
}
m_index++;
return true;
}
return false;
}
case OBJ_TABLE_LIST:
{
if(m_tables)
{
*db_name = Audit_formatter::table_get_db_name(m_tables);
*obj_name = Audit_formatter::table_get_name(m_tables);
if(obj_type)
{
//object is a view if it view command (alter_view, drop_view ..)
//and first object or view field is populated
if((m_firstTable && strstr(getCmdName(), "_view") != NULL) ||
Audit_formatter::table_is_view(m_tables))
{
*obj_type = "VIEW";
m_firstTable = false;
}
else
{
*obj_type = "TABLE";
}
}
m_tables = m_tables->next_global;
return true;
}
return false;
}
default :
return false;
}
}

956
src/audit_plugin.cc Executable file → Normal file

File diff suppressed because it is too large Load Diff

162
src/hot_patch.cc Executable file → Normal file
View File

@@ -1,5 +1,6 @@
#include "hot_patch.h"
#include "udis86.h"
#include <ctype.h>
#define UINT unsigned int
#define DWORD uint32_t
@@ -12,26 +13,107 @@
static const char * log_prefix = "Audit Plugin:";
#define protect(addr,len) (mprotect(addr,len,PROT_READ|PROT_EXEC))
static const unsigned long PAGE_SIZE = GETPAGESIZE() ;
//used to indicate how to do the protect/unprotect
static bool use_exec_prot = true;
static int protect(void *addr, size_t len)
{
int res = 0;
if(use_exec_prot)
{
res = mprotect(addr,len,PROT_READ|PROT_EXEC);
}
else //try doing in a 2 step fashion
{
mprotect(addr,len,PROT_READ);
res = mprotect(addr,len,PROT_READ|PROT_EXEC);
}
if(res)
{
sql_print_information(
"%s unable to protect mode: PROT_READ|PROT_EXEC. Page: %p, Size: %zu, errno: %d, res %d.",
log_prefix, (void *)addr, len, errno, res);
//fail only if nx bit is enabled
FILE * fp = fopen("/proc/cpuinfo", "r");
if(NULL == fp)
{
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.",
log_prefix, (void *)addr, len, errno);
return res;
}
char buff[1024] = {0};
const char * flags = "flags";
bool nxchecked = false;
while(fgets(buff, 1024, fp) != NULL)
{
char * line = buff;
//trim white space at start
while ((strlen(line) > 0) && (isspace(line[0])))
{
line++;
}
if(strncmp(line, flags, strlen(flags)) == 0)
{
nxchecked = true;
sql_print_information("%s cpuinfo flags line: %s. ",log_prefix, line);
if(strstr(line, " nx")) //nx enabled so fail
{
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.",
log_prefix, (void *)addr, len);
fclose(fp);
return res;
}
break;
}
}
fclose(fp);
if(!nxchecked) //we didn't find flags string for some reason
{
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.",
log_prefix, flags, (void *)addr, len);
return res;
}
}
return 0;
}
//will try to unprotect with PROT_READ|PROT_WRITE|PROT_EXEC. If fails (might happen under SELinux)
//will use PROT_READ|PROT_WRITE
static int unprotect(void *addr, size_t len)
{
static bool use_exec_prot = true;
{
int res;
if(use_exec_prot)
{
res = mprotect(addr,len,PROT_READ|PROT_WRITE|PROT_EXEC);
if(0 != res)
if(res)
{
sql_print_information(
"%s unable to unprotect. Page: 0x%lx, Size: %d, errno: %d. Using NO EXEC mode.",
log_prefix, (unsigned long)addr, len, errno);
"%s unable to unprotect. Page: %p, Size: %zu, errno: %d. Using NO EXEC mode.",
log_prefix, (void *)addr, len, errno);
use_exec_prot = false;
//do a sanity test that we can actually unprotect/protect and that nx bit is off
res = unprotect(addr, len);
if(res)
{
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.",
log_prefix, (void *)addr, len, errno);
return res;
}
res = protect(addr, len);
sql_print_information("%s protect res: %d", log_prefix, res);
if(res)
{
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.",
log_prefix, (void *)addr, len, errno);
return res;
}
}
else //all is good
{
@@ -39,11 +121,11 @@ static int unprotect(void *addr, size_t len)
}
}
res = mprotect(addr,len,PROT_READ|PROT_WRITE);
if(res != 0) //log the failure
if(0 != res) //log the failure
{
sql_print_error(
"%s unable to unprotect. Page: 0x%lx, Size: %d, errno: %d. Error.",
log_prefix, (unsigned long)addr, len, errno);
"%s unable to unprotect. Page: %p, Size: %zu, errno: %d. Error.",
log_prefix, (void *)addr, len, errno);
}
return res;
}
@@ -82,7 +164,6 @@ unsigned int jump_size()
static void WriteJump(void *pAddress, ULONG_PTR JumpTo)
{
DWORD dwOldProtect = 0;
DATATYPE_ADDRESS AddressPage = get_page_address(pAddress);
unprotect((void*)AddressPage, PAGE_SIZE);
@@ -104,7 +185,7 @@ static void WriteJump(void *pAddress, ULONG_PTR JumpTo)
#endif
//}
DWORD dwBuf = 0; // nessary othewrise the function fails
//DWORD dwBuf = 0; // nessary othewrise the function fails
protect((void*)AddressPage, PAGE_SIZE);
}
@@ -130,32 +211,61 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG
ud_set_input_buffer(&ud_obj, raw, MAX_INSTRUCTIONS);
ud_set_mode(&ud_obj, ASM_MODE);
ud_set_syntax(&ud_obj, UD_SYN_INTEL);
ud_set_pc(&ud_obj, targetFunction);
DWORD InstrSize = 0;
DATATYPE_ADDRESS trampolineFunctionPage = get_page_address((void*)trampolineFunction);
if(unprotect((void*)trampolineFunctionPage, PAGE_SIZE) != 0)
{
sql_print_error(
"%s unable to unprotect trampoline function page: 0x%lx. Aborting.",
log_prefix, trampolineFunctionPage);
"%s unable to unprotect trampoline function page: %p. Aborting.",
log_prefix, (void *)trampolineFunctionPage);
return false;
}
while (ud_disassemble(&ud_obj) && (strncmp (ud_insn_asm(&ud_obj),"invalid",7)!=0))
bool disassemble_valid = false;
while (ud_disassemble(&ud_obj))
{
if (InstrSize >= jump_size())
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);
memcpy((BYTE*)trampolineFunction + uCurrentSize,
(void *) pCurInstr, ud_insn_len (&ud_obj));
uCurrentSize += 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(
"%s unable to protect page. Error. Page: %p.",
log_prefix, (void *)trampolineFunctionPage);
return false;
}
if(!disassemble_valid) //something went wrong. log was written before so return false
{
return false;
}
protect((void*)trampolineFunctionPage, PAGE_SIZE);
WriteJump( (BYTE*)trampolineFunction + uCurrentSize, targetFunction + InstrSize);
WriteJump((void *) targetFunction, newFunction);
*trampolinesize = uCurrentSize;
@@ -173,8 +283,8 @@ static void UnhookFunction(ULONG_PTR Function,ULONG_PTR trampolineFunction , uns
if(unprotect((void*)FunctionPage, PAGE_SIZE) != 0)
{
sql_print_error(
"%s Unhook not able to unprotect function page: 0x%lx. Aborting.",
log_prefix, FunctionPage);
"%s Unhook not able to unprotect function page: %p. Aborting.",
log_prefix, (void * )FunctionPage);
return;
}
memcpy((void *) Function, (void*)trampolineFunction,trampolinesize);
@@ -201,7 +311,7 @@ static void UnhookFunction(ULONG_PTR Function,ULONG_PTR trampolineFunction , uns
int hot_patch_function (void* targetFunction, void* newFunction, void * trampolineFunction, unsigned int *trampolinesize, bool info_print)
{
DATATYPE_ADDRESS trampolinePage = get_page_address(trampolineFunction);
cond_info_print(info_print, "%s hot patching function: 0x%lx, trampolineFunction: 0x%lx trampolinePage: 0x%lx",log_prefix, (unsigned long)targetFunction, (unsigned long)trampolineFunction, (unsigned long)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,
(ULONG_PTR) trampolineFunction, trampolinesize))
{
@@ -230,7 +340,7 @@ void remove_hot_patch_function (void* targetFunction, void * trampolineFunction,
return;
}
DATATYPE_ADDRESS targetPage = get_page_address(targetFunction);
cond_info_print(info_print, "%s removing hot patching function: 0x%lx targetPage: 0x%lx trampolineFunction: 0x%lx",log_prefix, (unsigned long)targetFunction, (unsigned long)targetPage, (unsigned long)trampolineFunction);
cond_info_print(info_print, "%s removing hot patching function: %p targetPage: %p trampolineFunction: %p",log_prefix, (void *)targetFunction, (void *)targetPage, (void *)trampolineFunction);
UnhookFunction ((ULONG_PTR) targetFunction, (ULONG_PTR)trampolineFunction,trampolinesize);
return;
}

297
src/md5.cc Executable file
View File

@@ -0,0 +1,297 @@
/*
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
* MD5 Message-Digest Algorithm (RFC 1321).
*
* Homepage:
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
*
* Author:
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
*
* This software was written by Alexander Peslyak in 2001. No copyright is
* claimed, and the software is hereby placed in the public domain.
* In case this attempt to disclaim copyright and place the software in the
* public domain is deemed null and void, then the software is
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* (This is a heavily cut-down "BSD license".)
*
* This differs from Colin Plumb's older public domain implementation in that
* no exactly 32-bit integer data type is required (any 32-bit or wider
* unsigned integer data type will do), there's no compile-time endianness
* configuration, and the function prototypes match OpenSSL's. No code from
* Colin Plumb's implementation has been reused; this comment merely compares
* the properties of the two independent implementations.
*
* The primary goals of this implementation are portability and ease of use.
* It is meant to be fast, but not as fast as possible. Some known
* optimizations are not included to reduce source code size and avoid
* compile-time configuration.
*
* Modified: to have naming convention as used in MySQL 5.1 and 5.5
*/
#include <string.h>
#include "md5.h"
#if MYSQL_VERSION_ID >= 50600
/*
* The basic MD5 functions.
*
* F and G are optimized compared to their RFC 1321 definitions for
* architectures that lack an AND-NOT instruction, just like in Colin Plumb's
* implementation.
*/
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
/*
* The MD5 transformation for all four rounds.
*/
#define STEP(f, a, b, c, d, x, t, s) \
(a) += f((b), (c), (d)) + (x) + (t); \
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
(a) += (b);
/*
* SET reads 4 input bytes in little-endian byte order and stores them
* in a properly aligned word in host byte order.
*
* The check for little-endian architectures that tolerate unaligned
* memory accesses is just an optimization. Nothing will break if it
* doesn't work.
*/
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
#define SET(n) \
(*(MD5_u32plus *)&ptr[(n) * 4])
#define GET(n) \
SET(n)
#else
#define SET(n) \
(ctx->block[(n)] = \
(MD5_u32plus)ptr[(n) * 4] | \
((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
#define GET(n) \
(ctx->block[(n)])
#endif
/*
* This processes one or more 64-byte data blocks, but does NOT update
* the bit counters. There are no alignment requirements.
*/
static void *body(MD5_CTX *ctx, void *data, unsigned long size)
{
unsigned char *ptr;
MD5_u32plus a, b, c, d;
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
ptr = (unsigned char *)data;
a = ctx->a;
b = ctx->b;
c = ctx->c;
d = ctx->d;
do {
saved_a = a;
saved_b = b;
saved_c = c;
saved_d = d;
/* Round 1 */
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
/* Round 2 */
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
/* Round 3 */
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
/* Round 4 */
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
a += saved_a;
b += saved_b;
c += saved_c;
d += saved_d;
ptr += 64;
} while (size -= 64);
ctx->a = a;
ctx->b = b;
ctx->c = c;
ctx->d = d;
return ptr;
}
void MD5_Init(MD5_CTX *ctx)
{
ctx->a = 0x67452301;
ctx->b = 0xefcdab89;
ctx->c = 0x98badcfe;
ctx->d = 0x10325476;
ctx->lo = 0;
ctx->hi = 0;
}
void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size)
{
MD5_u32plus saved_lo;
unsigned long used, free;
saved_lo = ctx->lo;
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
ctx->hi++;
ctx->hi += size >> 29;
used = saved_lo & 0x3f;
if (used) {
free = 64 - used;
if (size < free) {
memcpy(&ctx->buffer[used], data, size);
return;
}
memcpy(&ctx->buffer[used], data, free);
data = (unsigned char *)data + free;
size -= free;
body(ctx, ctx->buffer, 64);
}
if (size >= 64) {
data = body(ctx, data, size & ~(unsigned long)0x3f);
size &= 0x3f;
}
memcpy(ctx->buffer, data, size);
}
void MD5_Final(unsigned char *result, MD5_CTX *ctx)
{
unsigned long used, free;
used = ctx->lo & 0x3f;
ctx->buffer[used++] = 0x80;
free = 64 - used;
if (free < 8) {
memset(&ctx->buffer[used], 0, free);
body(ctx, ctx->buffer, 64);
used = 0;
free = 64;
}
memset(&ctx->buffer[used], 0, free - 8);
ctx->lo <<= 3;
ctx->buffer[56] = ctx->lo;
ctx->buffer[57] = ctx->lo >> 8;
ctx->buffer[58] = ctx->lo >> 16;
ctx->buffer[59] = ctx->lo >> 24;
ctx->buffer[60] = ctx->hi;
ctx->buffer[61] = ctx->hi >> 8;
ctx->buffer[62] = ctx->hi >> 16;
ctx->buffer[63] = ctx->hi >> 24;
body(ctx, ctx->buffer, 64);
result[0] = ctx->a;
result[1] = ctx->a >> 8;
result[2] = ctx->a >> 16;
result[3] = ctx->a >> 24;
result[4] = ctx->b;
result[5] = ctx->b >> 8;
result[6] = ctx->b >> 16;
result[7] = ctx->b >> 24;
result[8] = ctx->c;
result[9] = ctx->c >> 8;
result[10] = ctx->c >> 16;
result[11] = ctx->c >> 24;
result[12] = ctx->d;
result[13] = ctx->d >> 8;
result[14] = ctx->d >> 16;
result[15] = ctx->d >> 24;
memset(ctx, 0, sizeof(*ctx));
}
#endif