From f28830d7443328a48fcd3c47fe661d4ef81fea7c Mon Sep 17 00:00:00 2001 From: BlackLight Date: Fri, 1 Oct 2010 19:32:34 +0200 Subject: [PATCH] Supporting alerts and packets info output to db --- Makefile.am | 1 + Makefile.in | 10 ++- alert_parser.c | 10 ++- db.h | 37 ++++++-- mysql.c | 156 ++++++++++++++++++++++++++-------- outdb.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++ postgresql.c | 144 +++++++++++++++++++++++++------ schemas/mysql.sql | 82 ++++++++++++++++++ spp_ai.c | 14 ++- spp_ai.h | 10 ++- 10 files changed, 594 insertions(+), 81 deletions(-) create mode 100644 outdb.c create mode 100644 schemas/mysql.sql diff --git a/Makefile.am b/Makefile.am index c778720..aecb027 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,7 @@ cluster.c \ correlation.c \ db.c \ mysql.c \ +outdb.c \ postgresql.c \ regex.c \ spp_ai.c \ diff --git a/Makefile.in b/Makefile.in index fc0da7e..7c74ecb 100644 --- a/Makefile.in +++ b/Makefile.in @@ -79,9 +79,9 @@ am_libsf_ai_preproc_la_OBJECTS = libsf_ai_preproc_la-alert_history.lo \ libsf_ai_preproc_la-alert_parser.lo \ libsf_ai_preproc_la-bayesian.lo libsf_ai_preproc_la-cluster.lo \ libsf_ai_preproc_la-correlation.lo libsf_ai_preproc_la-db.lo \ - libsf_ai_preproc_la-mysql.lo libsf_ai_preproc_la-postgresql.lo \ - libsf_ai_preproc_la-regex.lo libsf_ai_preproc_la-spp_ai.lo \ - libsf_ai_preproc_la-stream.lo + libsf_ai_preproc_la-mysql.lo libsf_ai_preproc_la-outdb.lo \ + libsf_ai_preproc_la-postgresql.lo libsf_ai_preproc_la-regex.lo \ + libsf_ai_preproc_la-spp_ai.lo libsf_ai_preproc_la-stream.lo nodist_libsf_ai_preproc_la_OBJECTS = \ libsf_ai_preproc_la-sf_dynamic_preproc_lib.lo \ libsf_ai_preproc_la-sfPolicyUserData.lo @@ -258,6 +258,7 @@ cluster.c \ correlation.c \ db.c \ mysql.c \ +outdb.c \ postgresql.c \ regex.c \ spp_ai.c \ @@ -395,6 +396,9 @@ libsf_ai_preproc_la-db.lo: db.c libsf_ai_preproc_la-mysql.lo: mysql.c $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-mysql.lo `test -f 'mysql.c' || echo '$(srcdir)/'`mysql.c +libsf_ai_preproc_la-outdb.lo: outdb.c + $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-outdb.lo `test -f 'outdb.c' || echo '$(srcdir)/'`outdb.c + libsf_ai_preproc_la-postgresql.lo: postgresql.c $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-postgresql.lo `test -f 'postgresql.c' || echo '$(srcdir)/'`postgresql.c diff --git a/alert_parser.c b/alert_parser.c index 8a7cf68..b0bd105 100644 --- a/alert_parser.c +++ b/alert_parser.c @@ -145,8 +145,10 @@ AI_file_alertparser_thread ( void* arg ) AI_snort_alert *alert = NULL; AI_snort_alert *tmp = NULL; BOOL in_alert = false; + pthread_t alerts_pool_thread; pthread_t serializer_thread; + pthread_t db_thread; /* Initialize the mutex lock, so nobody can read the alerts while we write there */ pthread_mutex_init ( &alert_mutex, NULL ); @@ -241,8 +243,6 @@ AI_file_alertparser_thread ( void* arg ) fseek ( alert_fp, 0, SEEK_END ); #endif - /* Set the lock flag to true until it's done with alert parsing */ - while ( !feof ( alert_fp )) { fgets ( line, sizeof(line), alert_fp ); @@ -285,6 +285,12 @@ AI_file_alertparser_thread ( void* arg ) if ( pthread_create ( &serializer_thread, NULL, AI_serializer_thread, alert ) != 0 ) _dpd.fatalMsg ( "Failed to create the alerts' serializer thread\n" ); + if ( config->outdbtype != outdb_none ) + { + if ( pthread_create ( &db_thread, NULL, AI_store_alert_to_db_thread, alert ) != 0 ) + _dpd.fatalMsg ( "Failed to create the alert to db storing thread\n" ); + } + in_alert = false; alert = NULL; } diff --git a/db.h b/db.h index 26b83f7..af1eced 100644 --- a/db.h +++ b/db.h @@ -27,14 +27,23 @@ typedef MYSQL_RES* DB_result; typedef MYSQL_ROW DB_row; - #define DB_init mysql_do_init - #define DB_query mysql_do_query - #define DB_num_rows mysql_num_rows - #define DB_fetch_row mysql_fetch_row - #define DB_free_result mysql_free_result - #define DB_close mysql_do_close + #define DB_init mysql_do_init + #define DB_is_init mysql_is_init + #define DB_query mysql_do_query + #define DB_num_rows mysql_num_rows + #define DB_fetch_row mysql_fetch_row + #define DB_free_result mysql_free_result + #define DB_escape_string mysql_do_escape_string + #define DB_close mysql_do_close + + #define DB_out_init mysql_do_out_init + #define DB_is_out_init mysql_is_out_init + #define DB_out_query mysql_do_out_query + #define DB_out_escape_string mysql_do_out_escape_string + #define DB_out_close mysql_do_out_close DB_result* DB_query ( const char* ); + DB_result* DB_out_query ( const char* ); #endif #ifdef HAVE_LIBPQ @@ -50,20 +59,32 @@ typedef char** DB_row; #define DB_init postgresql_do_init + #define DB_is_init postgresql_is_init #define DB_query postgresql_do_query #define DB_num_rows postgresql_num_rows #define DB_fetch_row postgresql_fetch_row #define DB_free_result postgresql_free_result #define DB_close postgresql_do_close + #define DB_out_init postgresql_do_out_init + #define DB_is_out_init postgresql_is_out_init + #define DB_out_query postgresql_do_out_query + #define DB_out_close postgresql_do_out_close + int DB_num_rows ( PSQL_result *res ); DB_row DB_fetch_row ( PSQL_result *res ); void DB_free_result ( PSQL_result *res ); DB_result DB_query ( const char* ); + DB_result DB_out_query ( const char* ); #endif - void* DB_init(); - void DB_close(); + void* DB_init(); + unsigned long DB_escape_string(); + void DB_close(); + + void* DB_out_init(); + unsigned long DB_out_escape_string(); + void DB_out_close(); #endif #endif diff --git a/mysql.c b/mysql.c index 59609ca..8416091 100644 --- a/mysql.c +++ b/mysql.c @@ -25,56 +25,146 @@ /** \defgroup mysql Module for the interface with a MySQL DBMS * @{ */ -PRIVATE MYSQL *db = NULL; +/***************************/ +/* Database descriptors */ +PRIVATE MYSQL *db = NULL; +PRIVATE MYSQL *outdb = NULL; +/***************************/ + +/*************************************************************/ +/* Private functions (operating on the database descriptors) */ + +PRIVATE BOOL +__mysql_is_init ( MYSQL *__DB ) +{ + return ( __DB != NULL ); +} + +PRIVATE void* +__mysql_do_init ( MYSQL **__DB, BOOL is_out ) +{ + if ( __mysql_is_init ( *__DB ) ) + return (void*) *__DB; + + if ( !( *__DB = (MYSQL*) malloc ( sizeof ( MYSQL )))) + return NULL; + + if ( !( mysql_init ( *__DB ))) + return NULL; + + if ( is_out ) + { + if ( !mysql_real_connect ( *__DB, config->outdbhost, config->outdbuser, config->outdbpass, NULL, 0, NULL, 0 )) + return NULL; + + if ( mysql_select_db ( *__DB, config->outdbname )) + return NULL; + } else { + if ( !mysql_real_connect ( *__DB, config->dbhost, config->dbuser, config->dbpass, NULL, 0, NULL, 0 )) + return NULL; + + if ( mysql_select_db ( *__DB, config->dbname )) + return NULL; + } + + return (void*) *__DB; +} + +PRIVATE MYSQL_RES* +__mysql_do_query ( MYSQL *__DB, const char *query ) +{ + MYSQL_RES *res = NULL; + + if ( mysql_query ( __DB, query )) + return NULL; + + if ( !( res = mysql_store_result ( __DB ))) + return NULL; + + return res; +} + +PRIVATE void +__mysql_do_close ( MYSQL **__DB ) +{ + if ( *__DB ) + mysql_close ( *__DB ); + + free ( *__DB ); + *__DB = NULL; +} + +/* End of private functions */ +/****************************/ + +/********************/ +/* Public functions */ + +BOOL +mysql_is_init () +{ + return __mysql_is_init ( db ); +} void* mysql_do_init () { - if ( !( db = (MYSQL*) malloc ( sizeof ( MYSQL )))) - return NULL; - - if ( !( mysql_init ( db ))) - return NULL; - - if ( !mysql_real_connect ( db, config->dbhost, config->dbuser, config->dbpass, NULL, 0, NULL, 0 )) - return NULL; - - if ( mysql_select_db ( db, config->dbname )) - return NULL; - - return (void*) db; + return __mysql_do_init ( &db, false ); } MYSQL_RES* mysql_do_query ( const char *query ) { - MYSQL_RES *res = NULL; + return __mysql_do_query ( db, query ); +} - if ( mysql_query ( db, query )) - { - mysql_close ( db ); - return NULL; - } - - if ( !( res = mysql_store_result ( db ))) - { - mysql_close ( db ); - return NULL; - } - - return res; +unsigned long +mysql_do_escape_string ( char **to, const char *from, unsigned long length ) +{ + return mysql_real_escape_string ( db, *to, from, length ); } void mysql_do_close () { - if ( db ) - mysql_close ( db ); - - free ( db ); - db = NULL; + __mysql_do_close ( &db ); } +/* Output database functions */ + +BOOL +mysql_is_out_init () +{ + return __mysql_is_init ( outdb ); +} + +void* +mysql_do_out_init () +{ + return __mysql_do_init ( &outdb, true ); +} + +MYSQL_RES* +mysql_do_out_query ( const char *query ) +{ + return __mysql_do_query ( outdb, query ); +} + +unsigned long +mysql_do_out_escape_string ( char **to, const char *from, unsigned long length ) +{ + return mysql_real_escape_string ( outdb, *to, from, length ); +} + +void +mysql_do_out_close () +{ + __mysql_do_close ( &outdb ); +} + +/* End of public functions */ +/***************************/ + /** @} */ #endif diff --git a/outdb.c b/outdb.c new file mode 100644 index 0000000..8dd107c --- /dev/null +++ b/outdb.c @@ -0,0 +1,211 @@ +/* + * ===================================================================================== + * + * Filename: outdb.c + * + * Description: Module for writing to a database the outputs (alerts, hyperalerts, + * clustered alerts, correlated alerts, alerts' TCP streams) from the + * preprocessor module + * + * Version: 0.1 + * Created: 30/09/2010 20:02:17 + * Revision: none + * Compiler: gcc + * + * Author: BlackLight (http://0x00.ath.cx), + * Licence: GNU GPL v.3 + * Company: DO WHAT YOU WANT CAUSE A PIRATE IS FREE, YOU ARE A PIRATE! + * + * ===================================================================================== + */ + +#include "spp_ai.h" + +/** \defgroup outdb Storing alerts, packets, clusters and correlations information on a database + * @{ */ + +#ifdef HAVE_DB + +#include "db.h" +#include +#include + +enum { ALERTS_TABLE, IPV4_HEADERS_TABLE, TCP_HEADERS_TABLE, PACKET_STREAMS_TABLE, CLUSTERED_ALERTS_TABLE, CORRELATED_ALERTS_TABLE, N_TABLES }; + +static const char *outdb_config[] = { + "ca_alerts", "ca_ipv4_headers", "ca_tcp_headers", + "ca_packet_streams", "ca_clustered_alerts", "ca_correlated_alerts" +}; + +PRIVATE pthread_mutex_t mutex; + +/** + * \brief Thread for storing an alert to the database + * \param arg Alert to be stored + */ + +void* +AI_store_alert_to_db_thread ( void *arg ) +{ + char srcip[INET_ADDRSTRLEN], dstip[INET_ADDRSTRLEN]; + char query[65535] = { 0 }; + unsigned char *pkt_data = NULL; + unsigned long latest_ip_hdr_id = 0, + latest_tcp_hdr_id = 0, + latest_alert_id = 0, + pkt_size = 0, + pkt_size_offset = 0; + + struct pkt_info *pkt = NULL; + DB_result res; + DB_row row; + AI_snort_alert *alert = (AI_snort_alert*) arg; + + pthread_mutex_init ( &mutex, NULL ); + + if ( !DB_out_init() ) + _dpd.fatalMsg ( "AIPreproc: Unable to connect to output database '%s'\n", config->outdbname ); + + pthread_mutex_lock ( &mutex ); + + inet_ntop ( AF_INET, &(alert->ip_src_addr), srcip, INET_ADDRSTRLEN ); + inet_ntop ( AF_INET, &(alert->ip_dst_addr), dstip, INET_ADDRSTRLEN ); + + /* Store the IP header information */ + memset ( query, 0, sizeof ( query )); + snprintf ( query, sizeof ( query ), "INSERT INTO %s (ip_tos, ip_len, ip_id, ip_ttl, ip_proto, ip_src_addr, ip_dst_addr) " + "VALUES (%u, %u, %u, %u, %u, '%s', '%s')", + outdb_config[IPV4_HEADERS_TABLE], + alert->ip_tos, + ntohs (alert->ip_len ), + ntohs (alert->ip_id ), + alert->ip_ttl, + alert->ip_proto, + srcip, + dstip ); + + DB_out_query ( query ); + + memset ( query, 0, sizeof ( query )); + snprintf ( query, sizeof ( query ), "SELECT MAX(ip_hdr_id) FROM %s", outdb_config[IPV4_HEADERS_TABLE] ); + + if ( !( res = (DB_result) DB_out_query ( query ))) + { + _dpd.logMsg ( "AIPreproc: Warning: error in executing query: '%s'\n", query ); + pthread_exit ((void*) 0); + } + + if ( !( row = (DB_row) DB_fetch_row ( res ))) + { + pthread_exit ((void*) 0); + } + + latest_ip_hdr_id = strtoul ( row[0], NULL, 10 ); + DB_free_result ( res ); + + if ( alert->ip_proto == IPPROTO_TCP || alert->ip_proto == IPPROTO_UDP ) + { + /* Store the TCP header information */ + memset ( query, 0, sizeof ( query )); + snprintf ( query, sizeof ( query ), "INSERT INTO %s (tcp_src_port, tcp_dst_port, tcp_seq, tcp_ack, tcp_flags, tcp_window, tcp_len) " + "VALUES (%u, %u, %u, %u, %u, %u, %u)", + outdb_config[TCP_HEADERS_TABLE], + ntohs (alert->tcp_src_port ), + ntohs (alert->tcp_dst_port ), + ntohl (alert->tcp_seq ), + ntohl (alert->tcp_ack ), + alert->tcp_flags, + ntohs (alert->tcp_window ), + ntohs (alert->tcp_len )); + + DB_out_query ( query ); + + memset ( query, 0, sizeof ( query )); + snprintf ( query, sizeof ( query ), "SELECT MAX(tcp_hdr_id) FROM %s", outdb_config[TCP_HEADERS_TABLE] ); + + if ( !( res = (DB_result) DB_out_query ( query ))) + { + _dpd.logMsg ( "AIPreproc: Warning: error in executing query: '%s'\n", query ); + pthread_exit ((void*) 0); + } + + if ( !( row = (DB_row) DB_fetch_row ( res ))) + { + pthread_exit ((void*) 0); + } + + latest_tcp_hdr_id = strtoul ( row[0], NULL, 10 ); + DB_free_result ( res ); + } + + memset ( query, 0, sizeof ( query )); + snprintf ( query, sizeof ( query ), "INSERT INTO %s (gid, sid, rev, priority, description, classification, timestamp, ip_hdr, tcp_hdr) " + "VALUES (%u, %u, %u, %u, '%s', '%s', from_unixtime('%lu'), %lu, %lu)", + outdb_config[ALERTS_TABLE], + alert->gid, + alert->sid, + alert->rev, + alert->priority, + ((alert->desc) ? alert->desc : ""), + ((alert->classification) ? alert->classification : ""), + alert->timestamp, + latest_ip_hdr_id, + ((alert->ip_proto == IPPROTO_TCP || alert->ip_proto == IPPROTO_UDP) ? latest_tcp_hdr_id : 0)); + + DB_out_query ( query ); + + memset ( query, 0, sizeof ( query )); + snprintf ( query, sizeof ( query ), "SELECT MAX(alert_id) FROM %s", outdb_config[ALERTS_TABLE] ); + + if ( !( res = (DB_result) DB_out_query ( query ))) + { + _dpd.logMsg ( "AIPreproc: Warning: error in executing query: '%s'\n", query ); + pthread_exit ((void*) 0); + } + + if ( !( row = (DB_row) DB_fetch_row ( res ))) + { + pthread_exit ((void*) 0); + } + + latest_alert_id = strtoul ( row[0], NULL, 10 ); + alert->alert_id = latest_alert_id; + DB_free_result ( res ); + + if ( alert->stream ) + { + for ( pkt = alert->stream; pkt; pkt = pkt->next ) + { + pkt_data = NULL; + pkt_size = pkt->pkt->pcap_cap_len; + pkt_size_offset = 0; + + if ( !( pkt_data = (unsigned char*) alloca ( 2 * (pkt->pkt->pcap_header->len + pkt->pkt->payload_size) + 1 ))) + _dpd.fatalMsg ( "AIPreproc: Fatal dynamic allocation memory at %s:%d\n", __FILE__, __LINE__ ); + + DB_out_escape_string ( &pkt_data, + pkt->pkt->pkt_data, + pkt->pkt->pcap_header->len + pkt->pkt->payload_size ); + + memset ( query, 0, sizeof ( query )); + snprintf ( query, sizeof ( query ), "INSERT INTO %s (alert_id, pkt_len, timestamp, content) " + "VALUES (%lu, %u, from_unixtime('%lu'), '%s')", + outdb_config[PACKET_STREAMS_TABLE], + latest_alert_id, + pkt->pkt->pcap_header->len + pkt->pkt->payload_size, + pkt->timestamp, + pkt_data ); + + DB_out_query ( query ); + } + } + + pthread_mutex_unlock ( &mutex ); + pthread_exit ((void*) 0); + return (void*) 0; +} + +#endif + +/** @} */ + diff --git a/postgresql.c b/postgresql.c index 089c03c..f788384 100644 --- a/postgresql.c +++ b/postgresql.c @@ -27,41 +27,78 @@ /** \defgroup postgresql Module for the interface with a PostgreSQL DBMS * @{ */ -PRIVATE PGconn *db = NULL; +/***************************/ +/* Database descriptors */ +PRIVATE PGconn *db = NULL; +PRIVATE PGconn *outdb = NULL; +/***************************/ -void* -postgresql_do_init () +/*************************************************************/ +/* Private functions (operating on the database descriptors) */ + +PRIVATE BOOL +__postgresql_is_init ( PGconn *__DB ) +{ + return ( __DB != NULL ); +} + +PRIVATE void* +__postgresql_do_init ( PGconn **__DB, BOOL is_out ) { char *conninfo = NULL; int conninfo_max_length = - ((config->dbhost) ? strlen ( config->dbhost ) : 0) + - ((config->dbuser) ? strlen ( config->dbuser ) : 0) + - ((config->dbpass) ? strlen ( config->dbpass ) : 0) + - ((config->dbname) ? strlen ( config->dbname ) : 0) + 100; + ( is_out ? + ((config->outdbhost) ? strlen ( config->outdbhost ) : 0) + + ((config->outdbuser) ? strlen ( config->outdbuser ) : 0) + + ((config->outdbpass) ? strlen ( config->outdbpass ) : 0) + + ((config->outdbname) ? strlen ( config->outdbname ) : 0) : + ((config->dbhost) ? strlen ( config->dbhost ) : 0) + + ((config->dbuser) ? strlen ( config->dbuser ) : 0) + + ((config->dbpass) ? strlen ( config->dbpass ) : 0) + + ((config->dbname) ? strlen ( config->dbname ) : 0)) + 100; + + if ( postgresql_is_init ( *__DB )) + return (void*) *__DB; + if ( !( conninfo = (char*) alloca ( conninfo_max_length ))) _dpd.fatalMsg ( "AIPreproc: Fatal dynamic memory allocation error at %s:%d\n", __FILE__, __LINE__ ); memset ( conninfo, 0, conninfo_max_length ); - snprintf ( conninfo, conninfo_max_length, "dbname=%s", config->dbname ); - if ( config->dbuser ) - sprintf ( conninfo, "%s user=%s", conninfo, config->dbuser ); + if ( is_out ) + { + snprintf ( conninfo, conninfo_max_length, "dbname=%s", config->outdbname ); - if ( config->dbpass ) - sprintf ( conninfo, "%s password=%s", conninfo, config->dbpass ); + if ( config->outdbuser ) + sprintf ( conninfo, "%s user=%s", conninfo, config->outdbuser ); - if ( config->dbhost ) - sprintf ( conninfo, "%s hostaddr=%s", conninfo, config->dbhost ); + if ( config->outdbpass ) + sprintf ( conninfo, "%s password=%s", conninfo, config->outdbpass ); - if ( PQstatus ( db = PQconnectdb ( conninfo )) != CONNECTION_OK ) + if ( config->outdbhost ) + sprintf ( conninfo, "%s hostaddr=%s", conninfo, config->outdbhost ); + } else { + snprintf ( conninfo, conninfo_max_length, "dbname=%s", config->dbname ); + + if ( config->dbuser ) + sprintf ( conninfo, "%s user=%s", conninfo, config->dbuser ); + + if ( config->dbpass ) + sprintf ( conninfo, "%s password=%s", conninfo, config->dbpass ); + + if ( config->dbhost ) + sprintf ( conninfo, "%s hostaddr=%s", conninfo, config->dbhost ); + } + + if ( PQstatus ( *__DB = PQconnectdb ( conninfo )) != CONNECTION_OK ) return NULL; - return (void*) db; + return (void*) *__DB; } -PSQL_result* -postgresql_do_query ( const char *query ) +PRIVATE PSQL_result* +__postgresql_do_query ( PGconn *__DB, const char *query ) { int i, j, ntuples, nfields; PSQL_result *res = NULL; @@ -69,9 +106,9 @@ postgresql_do_query ( const char *query ) if ( !( res = (PSQL_result*) malloc ( sizeof ( PSQL_result )))) _dpd.fatalMsg ( "AIPreproc: Fatal dynamic memory allocation error at %s:%d\n", __FILE__, __LINE__ ); - if ( PQresultStatus ( res->res = PQexec( db, query )) != PGRES_TUPLES_OK ) + if ( PQresultStatus ( res->res = PQexec( __DB, query )) != PGRES_TUPLES_OK ) { - PQfinish ( db ); + PQfinish ( __DB ); return NULL; } @@ -131,15 +168,74 @@ postgresql_free_result ( PSQL_result *res ) } } +PRIVATE void +__postgresql_do_close ( PGconn **__DB ) +{ + if ( *__DB ) + PQfinish ( *__DB ); + + *__DB = NULL; +} + +/* End of private functions */ +/****************************/ + +/********************/ +/* Public functions */ + +BOOL +postgresql_is_init () +{ + return __postgresql_is_init ( db ); +} + +void* +postgresql_do_init () +{ + return __postgresql_do_init ( &db, false ); +} + +PSQL_result* +postgresql_do_query ( const char *query ) +{ + return __postgresql_do_query ( db, query ); +} + void postgresql_do_close () { - if ( db ) - PQfinish ( db ); - - db = NULL; + __postgresql_do_close ( &db ); } +/* Output database functions */ + +BOOL +postgresql_is_out_init () +{ + return __postgresql_is_init ( outdb ); +} + +void* +postgresql_do_out_init () +{ + return __postgresql_do_init ( &outdb, true ); +} + +PSQL_result* +postgresql_do_out_query ( const char *query ) +{ + return __postgresql_do_query ( outdb, query ); +} + +void +postgresql_do_out_close () +{ + __postgresql_do_close ( &outdb ); +} + +/* End of public functions */ +/***************************/ + /* @} */ #endif diff --git a/schemas/mysql.sql b/schemas/mysql.sql new file mode 100644 index 0000000..031a507 --- /dev/null +++ b/schemas/mysql.sql @@ -0,0 +1,82 @@ +DROP TABLE IF EXISTS ca_ipv4_headers; +CREATE TABLE ca_ipv4_headers ( + ip_hdr_id integer auto_increment, + ip_tos integer, + ip_len integer, + ip_id integer, + ip_ttl integer, + ip_proto integer, + ip_src_addr varchar(32), + ip_dst_addr varchar(32), + + primary key(ip_hdr_id) +); + +DROP TABLE IF EXISTS ca_tcp_headers; +CREATE TABLE ca_tcp_headers ( + tcp_hdr_id integer auto_increment, + tcp_src_port integer, + tcp_dst_port integer, + tcp_seq integer, + tcp_ack integer, + tcp_flags integer, + tcp_window integer, + tcp_len integer, + + primary key(tcp_hdr_id) +); + +DROP TABLE IF EXISTS ca_packet_streams; +CREATE TABLE ca_packet_streams ( + pkt_id integer auto_increment, + alert_id integer, + pkt_len integer, + timestamp datetime, + content longblob, + + primary key(pkt_id), + foreign key(alert_id) references ca_alerts(alert_id) +); + +DROP TABLE IF EXISTS ca_alerts; +CREATE TABLE ca_alerts ( + alert_id integer auto_increment, + gid integer, + sid integer, + rev integer, + priority integer, + description varchar(255), + classification varchar(255), + timestamp datetime, + ip_hdr integer, + tcp_hdr integer, + cluster_id integer, + + primary key(alert_id), + foreign key(ip_hdr) references ca_ip_headers(ip_hdr_id), + foreign key(tcp_hdr) references ca_tcp_headers(tcp_hdr_id), + foreign key(cluster_id) references ca_clustered_alerts(cluster_id) +); + +DROP TABLE IF EXISTS ca_clustered_alerts; +CREATE TABLE ca_clustered_alerts ( + cluster_id integer auto_increment, + clustered_srcip varchar(255) default null, + clustered_dstip varchar(255) default null, + clustered_srcport varchar(255) default null, + clustered_dstport varchar(255) default null, + + primary key(cluster_id) +); + +DROP TABLE IF EXISTS ca_correlated_alerts; +CREATE TABLE ca_correlated_alerts ( + cluster1 integer, + cluster2 integer, + correlation_coeff double, + + primary key(cluster1, cluster2), + foreign key(cluster1) references ca_clustered_alerts(cluster_id), + foreign key(cluster2) references ca_clustered_alerts(cluster_id) +); + diff --git a/spp_ai.c b/spp_ai.c index d06fa9b..d911a48 100644 --- a/spp_ai.c +++ b/spp_ai.c @@ -654,8 +654,9 @@ static AI_config * AI_parse(char *args) _dpd.logMsg(" Reading alerts from the database %s\n", config->dbname ); } - /* Parsing output_database option */ + config->outdbtype = outdb_none; + if ( preg_match ( "\\s*output_database\\s*\\(\\s*([^\\)]+)\\)", args, &matches, &nmatches ) > 0 ) { if ( ! has_database_output ) @@ -671,24 +672,21 @@ static AI_config * AI_parse(char *args) if ( preg_match ( "type\\s*=\\s*\"([^\"]+)\"", match, &matches, &nmatches ) > 0 ) { - if ( strcasecmp ( matches[0], "mysql" ) && strcasecmp ( matches[0], "postgresql" )) - { - _dpd.fatalMsg ( "AIPreproc: Not supported database '%s' (supported types: mysql, postgresql)\n", matches[0] ); - } - if ( !strcasecmp ( matches[0], "mysql" )) { #ifndef HAVE_LIBMYSQLCLIENT _dpd.fatalMsg ( "AIPreproc: mysql output set in 'output_database' option but the module was not compiled through --with-mysql option\n" ); #else - config->outdbtype = mysql; + config->outdbtype = outdb_mysql; #endif } else if ( !strcasecmp ( matches[0], "postgresql" )) { #ifndef HAVE_LIBPQ _dpd.fatalMsg ( "AIPreproc: postgresql output set in 'output_database' option but the module was not compiled through --with-postgresql option\n" ); #else - config->outdbtype = postgresql; + config->outdbtype = outdb_postgresql; #endif + } else { + _dpd.fatalMsg ( "AIPreproc: Not supported database '%s' (supported types: mysql, postgresql)\n", matches[0] ); } for ( i=0; i < nmatches; i++ ) diff --git a/spp_ai.h b/spp_ai.h index 5aac24f..447a10f 100644 --- a/spp_ai.h +++ b/spp_ai.h @@ -206,7 +206,7 @@ typedef struct /** Output database type, if clustered alerts and * correlations are saved to a database as well */ - enum { mysql, postgresql } outdbtype; + enum { outdb_none, outdb_mysql, outdb_postgresql, OUTDBTYPE_NUM } outdbtype; /** Output database name, if clustered alerts and * correlations are saved to a database as well */ @@ -338,6 +338,10 @@ typedef struct _AI_snort_alert { /** Number of derived alerts */ unsigned int n_derived_alerts; + + /** Alert ID on the database, if the alerts + * are stored on a database as well */ + unsigned long int alert_id; } AI_snort_alert; /*****************************************************************/ /** Key for the AI_alert_event structure, containing the Snort ID of the alert */ @@ -355,8 +359,6 @@ typedef struct _AI_alert_event { struct _AI_alert_event *next; UT_hash_handle hh; } AI_alert_event; -/*****************************************************************/ - int preg_match ( const char*, char*, char***, int* ); char* str_replace ( char*, char*, char *); @@ -389,6 +391,8 @@ const AI_alert_event* AI_get_alert_events_by_key ( AI_alert_event_key ); unsigned int AI_get_history_alert_number (); double AI_alert_bayesian_correlation ( AI_snort_alert *a, AI_snort_alert *b ); +void* AI_store_alert_to_db_thread ( void* ); + /** Function pointer to the function used for getting the alert list (from log file, db, ...) */ extern AI_snort_alert* (*get_alerts)(void);