Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

5210 lines (4416 sloc) 139.095 kb
/*
** spo_database.c
**
** Portions Copyright (C) 2000,2001,2002 Carnegie Mellon University
** Copyright (C) 2001 Jed Pickel <jed@pickel.net>
** Portions Copyright (C) 2001 Andrew R. Baker <andrewb@farm9.com>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License Version 2 as
** published by the Free Software Foundation. You may not use, modify or
** distribute this program under any other version of the GNU General
** Public License.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* Maintainers : The Barnyard2 Team <firnsy@gmail.com> <beenph@gmail.com>
* Past Maintainer: Roman Danyliw <rdd@cert.org>, <roman@danyliw.com>
* Originally written by Jed Pickel <jed@pickel.net> (2000-2001)
*
* See the doc/README.database file with this distribution
* documentation or the snortdb web site for configuration
* information
*
* Special thanks to: Rusell Fuleton <russell.fulton@gmail.com> for helping us stress test
* this in production for us.
*
*/
/******** fatals *******************************************************/
/* these strings deliberately break fatal error messages into
chunks with lengths < 509 to keep ISO C89 compilers happy
*/
static const char* FATAL_NO_SENSOR_1 =
" When this plugin starts, a SELECT query is run to find the sensor id for the\n"
" currently running sensor. If the sensor id is not found, the plugin will run\n"
" an INSERT query to insert the proper data and generate a new sensor id. Then a\n"
" SELECT query is run to get the newly allocated sensor id. If that fails then\n"
" this error message is generated.\n";
static const char* FATAL_NO_SENSOR_2 =
" Some possible causes for this error are:\n"
" * the user does not have proper INSERT or SELECT privileges\n"
" * the sensor table does not exist\n"
"\n"
" If you are _absolutely_ certain that you have the proper privileges set and\n"
" that your database structure is built properly please let me know if you\n"
" continue to get this error. You can contact me at (roman@danyliw.com).\n";
static const char* FATAL_BAD_SCHEMA_1 =
"database: The underlying database has not been initialized correctly. This\n"
" version of Snort requires version %d of the DB schema. Your DB\n"
" doesn't appear to have any records in the 'schema' table.\n%s";
static const char* FATAL_BAD_SCHEMA_2 =
" Please re-run the appropriate DB creation script (e.g. create_mysql,\n"
" create_postgresql, create_oracle, create_mssql) located in the\n"
" contrib\\ directory.\n\n"
" See the database documentation for cursory details (doc/README.database).\n"
" and the URL to the most recent database plugin documentation.\n";
static const char* FATAL_OLD_SCHEMA_1 =
"database: The underlying database seems to be running an older version of\n"
" the DB schema (current version=%d, required minimum version= %d).\n\n"
" If you have an existing database with events logged by a previous\n"
" version of snort, this database must first be upgraded to the latest\n"
" schema (see the snort-users mailing list archive or DB plugin\n"
" documention for details).\n%s\n";
static const char* FATAL_OLD_SCHEMA_2 =
" If migrating old data is not desired, merely create a new instance\n"
" of the snort database using the appropriate DB creation script\n"
" (e.g. create_mysql, create_postgresql, create_oracle, create_mssql)\n"
" located in the contrib\\ directory.\n\n"
" See the database documentation for cursory details (doc/README.database).\n"
" and the URL to the most recent database plugin documentation.\n";
static const char* FATAL_NO_SUPPORT_1 =
"If this build of snort was obtained as a binary distribution (e.g., rpm,\n"
"or Windows), then check for alternate builds that contains the necessary\n"
"'%s' support.\n\n"
"If this build of snort was compiled by you, then re-run the\n"
"the ./configure script using the '--with-%s' switch.\n"
"For non-standard installations of a database, the '--with-%s=DIR'\n%s";
static const char* FATAL_NO_SUPPORT_2 =
"syntax may need to be used to specify the base directory of the DB install.\n\n"
"See the database documentation for cursory details (doc/README.database).\n"
"and the URL to the most recent database plugin documentation.\n";
#include "output-plugins/spo_database.h"
void DatabaseCleanSelect(DatabaseData *data)
{
if( (data != NULL) &&
(data->SQL_SELECT) != NULL &&
(data->SQL_SELECT_SIZE > 0))
{
memset(data->SQL_SELECT,'\0',data->SQL_SELECT_SIZE);
}
return;
}
void DatabaseCleanInsert(DatabaseData *data )
{
if( (data != NULL) &&
(data->SQL_INSERT) != NULL &&
(data->SQL_INSERT_SIZE > 0))
{
memset(data->SQL_INSERT,'\0',data->SQL_INSERT_SIZE);
}
return;
}
/* SQLQueryList Funcs */
u_int32_t SQL_Initialize(DatabaseData *data)
{
u_int32_t x = 0;
if(data == NULL)
{
/* XXX */
return 1;
}
data->SQL.query_total = MAX_SQL_QUERY_OPS;
if( (data->SQL.query_array =(char **)SnortAlloc( (sizeof(char *) * data->SQL.query_total))) == NULL)
{
/* XXX */
return 1;
}
for(x = 0 ; x < data->SQL.query_total ; x++)
{
if( (data->SQL.query_array[x] = SnortAlloc( (sizeof(char) * MAX_QUERY_LENGTH ))) == NULL)
{
/* XXX */
return 1;
}
}
return 0;
}
u_int32_t SQL_Finalize(DatabaseData *data)
{
u_int32_t x = 0;
if(data == NULL)
{
/* XXX */
return 1;
}
for(x = 0 ; x < data->SQL.query_total ; x++)
{
if(data->SQL.query_array[x] != NULL)
{
free(data->SQL.query_array[x]);
data->SQL.query_array[x]= NULL;
}
}
if( data->SQL.query_array != NULL)
{
free(data->SQL.query_array);
data->SQL.query_array = NULL;
}
return 0;
}
char *SQL_GetNextQuery(DatabaseData *data)
{
char *ret_query = NULL;
if(data == NULL)
{
/* XXX */
return NULL;
}
if( data->SQL.query_count < data->SQL.query_total)
{
ret_query = data->SQL.query_array[data->SQL.query_count];
data->SQL.query_count++;
return ret_query;
}
return NULL;
}
char *SQL_GetQueryByPos(DatabaseData *data,u_int32_t pos)
{
if( (data == NULL) ||
pos > data->SQL.query_total)
{
/* XXX */
return NULL;
}
if(data->SQL.query_array[pos] != NULL)
{
return data->SQL.query_array[pos];
}
return NULL;
}
u_int32_t SQL_GetMaxQuery(DatabaseData *data)
{
if(data == NULL)
{
/* XXX */
return 0;
}
return data->SQL.query_count;
}
u_int32_t SQL_Cleanup(DatabaseData *data)
{
u_int32_t x = 0;
if(data == NULL)
{
/* XXX */
return 1;
}
if(data->SQL.query_count)
{
for(x = 0; x < data->SQL.query_count ; x++)
{
memset(data->SQL.query_array[x],'\0',(sizeof(char) * MAX_QUERY_LENGTH));
}
data->SQL.query_count = 0;
}
return 0;
}
/* SQLQueryList Funcs */
/*******************************************************************************
* Function: SetupDatabase()
*
* Purpose: Registers the output plugin keyword and initialization
* function into the output plugin list. This is the function that
* gets called from InitOutputPlugins() in plugbase.c.
*
* Arguments: None.
*
* Returns: void function
*
******************************************************************************/
void DatabaseSetup(void)
{
/* link the preprocessor keyword to the init function in
the preproc list */
/* CHECKME: -elz I think it should also support OUTPUT_TYPE_FLAG__LOG.. */
RegisterOutputPlugin("database", OUTPUT_TYPE_FLAG__ALERT, DatabaseInit);
DEBUG_WRAP(DebugMessage(DEBUG_INIT, "database(debug): database plugin is registered...\n"););
}
#ifndef DB_CHECK_TABLES
#define DB_CHECK_TABLES 7
#endif /* DB_CHECK_TABLES */
#ifndef DB_TABLE_NAME_LEN
#define DB_TABLE_NAME_LEN 20
#endif /* DB_TABLE_NAME_LEN */
/*
* Since it is possible that an error occured and that we could have an event_id out of sync
* or that a human/automated action could have cleaned the database but missed some old data
* we query every table where cid for this sid(sensor_id) is present and get the latest (cid)
* incident_id possible to start the process.
*/
u_int32_t SynchronizeEventId(DatabaseData *data)
{
if(data == NULL)
{
/* XXX */
return 1;
}
u_int32_t c_cid = 0;
u_int32_t num_tables = 7;
u_int32_t itr = 0;
char table_array[DB_CHECK_TABLES][DB_TABLE_NAME_LEN] = {"data","event","icmphdr","iphdr","opt","tcphdr","udphdr"};
if( GetLastCid(data, data->sid,(u_int32_t *)&data->cid))
{
/* XXX*/
return 1;
}
for(itr = 0; itr < num_tables ; itr++)
{
c_cid = 0;
DatabaseCleanSelect(data);
if(SnortSnprintf(data->SQL_SELECT,data->SQL_SELECT_SIZE,
"SELECT MAX(cid) FROM %s WHERE sid='%u';",
table_array[itr],
data->sid))
{
LogMessage("database: [%s()], was unable to build query \n",
__FUNCTION__);
return 1;
}
if(Select(data->SQL_SELECT,data,(u_int32_t *)&c_cid))
{
LogMessage("database: [%s()]: Problems executing [%s] \n",
__FUNCTION__,
data->SQL_SELECT);
}
if(c_cid > data->cid)
{
LogMessage("INFO database: Table [%s] had a more rescent cid [%u] using it. \n",
table_array[itr],
c_cid);
LogMessage("\t Using cid [%u] instead of [%u]\n",
c_cid,
data->cid);
data->cid = c_cid;
}
}
data->cid++;
if( UpdateLastCid(data, data->sid, data->cid) < 0 )
{
FatalError("database Unable to construct query - output error or truncation\n");
}
if( GetLastCid(data, data->sid,(u_int32_t *)&c_cid))
{
/* XXX*/
return 1;
}
if(c_cid != data->cid)
{
FatalError("database [%s()]: Something is wrong with the sensor table, you "
"might have two process updating it...bailing\n",
__FUNCTION__);
}
return 0;
}
void DatabasePluginPrintData(DatabaseData *data)
{
/* print out and test the capability of this plugin */
{
char database_support_buf[100];
char database_in_use_buf[100];
database_support_buf[0] = '\0';
database_in_use_buf[0] = '\0';
/* These strings will not overflow the buffers */
#ifdef ENABLE_MYSQL
snprintf(database_support_buf, sizeof(database_support_buf),
"database: compiled support for (%s)", KEYWORD_MYSQL);
if (data->dbtype_id == DB_MYSQL)
snprintf(database_in_use_buf, sizeof(database_in_use_buf),
"database: configured to use %s", KEYWORD_MYSQL);
#endif
#ifdef ENABLE_POSTGRESQL
snprintf(database_support_buf, sizeof(database_support_buf),
"database: compiled support for (%s)", KEYWORD_POSTGRESQL);
if (data->dbtype_id == DB_POSTGRESQL)
snprintf(database_in_use_buf, sizeof(database_in_use_buf),
"database: configured to use %s", KEYWORD_POSTGRESQL);
#endif
#ifdef ENABLE_ODBC
snprintf(database_support_buf, sizeof(database_support_buf),
"database: compiled support for (%s)", KEYWORD_ODBC);
if (data->dbtype_id == DB_ODBC)
snprintf(database_in_use_buf, sizeof(database_in_use_buf),
"database: configured to use %s", KEYWORD_ODBC);
#endif
#ifdef ENABLE_ORACLE
snprintf(database_support_buf, sizeof(database_support_buf),
"database: compiled support for (%s)", KEYWORD_ORACLE);
if (data->dbtype_id == DB_ORACLE)
snprintf(database_in_use_buf, sizeof(database_in_use_buf),
"database: configured to use %s", KEYWORD_ORACLE);
#endif
#ifdef ENABLE_MSSQL
snprintf(database_support_buf, sizeof(database_support_buf),
"database: compiled support for (%s)", KEYWORD_MSSQL);
if (data->dbtype_id == DB_MSSQL)
snprintf(database_in_use_buf, sizeof(database_in_use_buf),
"database: configured to use %s", KEYWORD_MSSQL);
#endif
LogMessage("%s\n", database_support_buf);
LogMessage("%s\n", database_in_use_buf);
}
LogMessage("database: schema version = %d\n", data->DBschema_version);
if (data->host != NULL)
LogMessage("database: host = %s\n", data->host);
if (data->port != NULL)
LogMessage("database: port = %s\n", data->port);
if (data->user != NULL)
LogMessage("database: user = %s\n", data->user);
if (data->dbname != NULL)
LogMessage("database: database name = %s\n", data->dbname);
if (data->sensor_name != NULL)
LogMessage("database: sensor name = %s\n", data->sensor_name);
LogMessage("database: sensor id = %u\n", data->sid);
LogMessage("database: sensor cid = %u\n", data->cid);
if (data->encoding == ENCODING_HEX)
{
LogMessage("database: data encoding = %s\n", KEYWORD_ENCODING_HEX);
}
else if (data->encoding == ENCODING_BASE64)
{
LogMessage("database: data encoding = %s\n", KEYWORD_ENCODING_BASE64);
}
else
{
LogMessage("database: data encoding = %s\n", KEYWORD_ENCODING_ASCII);
}
if (data->detail == DETAIL_FULL)
{
LogMessage("database: detail level = %s\n", KEYWORD_DETAIL_FULL);
}
else
{
LogMessage("database: detail level = %s\n", KEYWORD_DETAIL_FAST);
}
if (data->ignore_bpf)
{
LogMessage("database: ignore_bpf = %s\n", KEYWORD_IGNOREBPF_YES);
}
else
{
LogMessage("database: ignore_bpf = %s\n", KEYWORD_IGNOREBPF_NO);
}
#ifdef ENABLE_MYSQL
if (data->dbRH[data->dbtype_id].ssl_key != NULL)
LogMessage("database: ssl_key = %s\n", data->dbRH[data->dbtype_id].ssl_key);
if (data->dbRH[data->dbtype_id].ssl_cert != NULL)
LogMessage("database: ssl_cert = %s\n", data->dbRH[data->dbtype_id].ssl_cert);
if (data->dbRH[data->dbtype_id].ssl_ca != NULL)
LogMessage("database: ssl_ca = %s\n", data->dbRH[data->dbtype_id].ssl_ca);
if (data->dbRH[data->dbtype_id].ssl_ca_path != NULL)
LogMessage("database: ssl_ca_path = %s\n", data->dbRH[data->dbtype_id].ssl_ca_path);
if (data->dbRH[data->dbtype_id].ssl_cipher != NULL)
LogMessage("database: ssl_cipher = %s\n", data->dbRH[data->dbtype_id].ssl_cipher);
#endif /* ENABLE_MYSQL */
#ifdef ENABLE_POSTGRESQL
if (data->dbRH[data->dbtype_id].ssl_mode != NULL)
LogMessage("database: ssl_mode = %s\n", data->dbRH[data->dbtype_id].ssl_mode);
#endif /* ENABLE_POSTGRESQL */
if(data->facility != NULL)
{
LogMessage("database: using the \"%s\" facility\n",data->facility);
}
return;
}
/*******************************************************************************
* Function: DatabaseInit(char *)
*
* Purpose: Calls the argument parsing function, performs final setup on data
* structs, links the preproc function into the function list.
*
* Arguments: args => ptr to argument string
*
* Returns: void function
*
******************************************************************************/
void DatabaseInit(char *args)
{
DatabaseData *data = NULL;
/* parse the argument list from the rules file */
data = InitDatabaseData(args);
data->tz = GetLocalTimezone();
ParseDatabaseArgs(data);
/* Meanwhile */
data->dbRH[data->dbtype_id].dbdata = data;
/* Meanwhile */
switch(data->dbtype_id)
{
#ifdef ENABLE_MYSQL
case DB_MYSQL:
data->dbRH[data->dbtype_id].dbConnectionStatus = dbConnectionStatusMYSQL;
data->dbRH[data->dbtype_id].dbConnectionCount = 0;
break;
#endif /* ENABLE_MYSQL */
#ifdef ENABLE_POSTGRESQL
case DB_POSTGRESQL:
data->dbRH[data->dbtype_id].dbConnectionStatus = dbConnectionStatusPOSTGRESQL;
data->dbRH[data->dbtype_id].dbConnectionCount = 0;
break;
#endif /* ENABLE_POSTGRESQL */
#ifdef ENABLE_ODBC
case DB_ODBC:
data->dbRH[data->dbtype_id].dbConnectionStatus = dbConnectionStatusODBC;
data->dbRH[data->dbtype_id].dbConnectionCount = 0;
break;
#endif /* ENABLE ODBC */
#ifdef ENABLE_ORACLE
#ifdef ENABLE_MSSQL
case DB_MSSQL:
case DB_ORACLE:
FatalError("database The database family you want to use is currently not supported by this build \n");
break;
#endif /* ENABLE MSSQL */
#endif /* ENABLE ORACLE */
default:
FatalError("database Unknown database type defined: [%lu] \n",data->dbtype_id);
break;
}
/* Add the processor function into the function list */
if (strncasecmp(data->facility, "log", 3) == 0)
{
AddFuncToOutputList(Database, OUTPUT_TYPE__LOG, data);
}
else
{
AddFuncToOutputList(Database, OUTPUT_TYPE__ALERT, data);
}
AddFuncToRestartList(SpoDatabaseCleanExitFunction, data);
AddFuncToCleanExitList(SpoDatabaseCleanExitFunction, data);
AddFuncToPostConfigList(DatabaseInitFinalize, data);
/* Set the size of the buffers here */
data->SQL_INSERT_SIZE = (MAX_QUERY_LENGTH * sizeof(char));
data->SQL_SELECT_SIZE = (MAX_QUERY_LENGTH * sizeof(char));
if( (data->SQL_INSERT = malloc(data->SQL_INSERT_SIZE)) == NULL)
{
/* XXX */
FatalError("database [%s()], unable to allocate SQL_INSERT memory, bailing \n",
__FUNCTION__);
}
if ( (data->SQL_SELECT = malloc(data->SQL_SELECT_SIZE)) == NULL)
{
/* XXX */
FatalError("database [%s()], unable to allocate SQL_SELECT memory, bailing \n",
__FUNCTION__);
}
DatabaseCleanSelect(data);
DatabaseCleanInsert(data);
return;
}
u_int32_t DatabasePluginInitializeSensor(DatabaseData *data)
{
u_int32_t retval = 0;
char * escapedSensorName = NULL;
char * escapedInterfaceName = NULL;
char * escapedBPFFilter = NULL;
if(data == NULL)
{
/* XXX */
return 1;
}
/* find a unique name for sensor if one was not supplied as an option */
if(!data->sensor_name)
{
data->sensor_name = GetUniqueName(PRINT_INTERFACE(barnyard2_conf->interface));
if ( data->sensor_name )
{
if( data->sensor_name[strlen(data->sensor_name)-1] == '\n' )
{
data->sensor_name[strlen(data->sensor_name)-1] = '\0';
}
}
}
escapedSensorName = snort_escape_string(data->sensor_name, data);
escapedInterfaceName = snort_escape_string(PRINT_INTERFACE(barnyard2_conf->interface), data);
if( data->ignore_bpf == 0 )
{
if(barnyard2_conf->bpf_filter == NULL)
{
DatabaseCleanInsert(data);
if( (SnortSnprintf(data->SQL_INSERT, data->SQL_INSERT_SIZE,
"INSERT INTO sensor (hostname, interface, detail, encoding, last_cid) "
"VALUES ('%s','%s',%u,%u, 0);",
escapedSensorName, escapedInterfaceName,
data->detail, data->encoding)) != SNORT_SNPRINTF_SUCCESS)
{
/* XXX */
retval = 1;
goto exit_funct;
}
DatabaseCleanSelect(data);
if( (SnortSnprintf(data->SQL_SELECT,data->SQL_SELECT_SIZE,
"SELECT sid "
" FROM sensor "
" WHERE hostname = '%s' "
" AND interface = '%s' "
" AND detail = %u "
" AND encoding = %u "
" AND filter IS NULL",
escapedSensorName, escapedInterfaceName,
data->detail, data->encoding)) != SNORT_SNPRINTF_SUCCESS)
{
/* XXX */
retval = 1;
goto exit_funct;
}
}
else
{
escapedBPFFilter = snort_escape_string(barnyard2_conf->bpf_filter, data);
DatabaseCleanInsert(data);
if( (SnortSnprintf(data->SQL_INSERT, data->SQL_INSERT_SIZE,
"INSERT INTO sensor (hostname, interface, filter, detail, encoding, last_cid) "
"VALUES ('%s','%s','%s',%u,%u, 0);",
escapedSensorName, escapedInterfaceName,
escapedBPFFilter, data->detail, data->encoding)) != SNORT_SNPRINTF_SUCCESS)
{
retval = 1;
goto exit_funct;
}
DatabaseCleanSelect(data);
if( (SnortSnprintf(data->SQL_SELECT,data->SQL_SELECT_SIZE,
"SELECT sid "
" FROM sensor "
" WHERE hostname = '%s' "
" AND interface = '%s' "
" AND filter ='%s' "
" AND detail = %u "
" AND encoding = %u ",
escapedSensorName, escapedInterfaceName,
escapedBPFFilter, data->detail, data->encoding)) != SNORT_SNPRINTF_SUCCESS)
{
/* XXX */
retval = 1;
goto exit_funct;
}
}
}
else /* ( data->ignore_bpf == 1 ) */
{
if(barnyard2_conf->bpf_filter == NULL)
{
DatabaseCleanInsert(data);
if( (SnortSnprintf(data->SQL_INSERT, data->SQL_INSERT_SIZE,
"INSERT INTO sensor (hostname, interface, detail, encoding, last_cid) "
"VALUES ('%s','%s',%u,%u, 0);",
escapedSensorName, escapedInterfaceName,
data->detail, data->encoding)) != SNORT_SNPRINTF_SUCCESS)
{
/* XXX */
retval = 1;
goto exit_funct;
}
DatabaseCleanSelect(data);
if( (SnortSnprintf(data->SQL_SELECT,data->SQL_SELECT_SIZE,
"SELECT sid "
" FROM sensor "
" WHERE hostname = '%s' "
" AND interface = '%s' "
" AND detail = %u "
" AND encoding = %u",
escapedSensorName, escapedInterfaceName,
data->detail, data->encoding)) != SNORT_SNPRINTF_SUCCESS)
{
/* XXX */
retval = 1;
goto exit_funct;
}
}
else
{
escapedBPFFilter = snort_escape_string(barnyard2_conf->bpf_filter, data);
DatabaseCleanInsert(data);
if( (SnortSnprintf(data->SQL_INSERT, data->SQL_INSERT_SIZE,
"INSERT INTO sensor (hostname, interface, filter, detail, encoding, last_cid) "
"VALUES ('%s','%s','%s',%u,%u, 0);",
escapedSensorName, escapedInterfaceName,
escapedBPFFilter, data->detail, data->encoding)) != SNORT_SNPRINTF_SUCCESS)
{
/* XXX */
retval = 1;
goto exit_funct;
}
DatabaseCleanSelect(data);
if( (SnortSnprintf(data->SQL_SELECT,data->SQL_SELECT_SIZE,
"SELECT sid "
" FROM sensor "
" WHERE hostname = '%s' "
" AND interface = '%s' "
" AND detail = %u "
" AND encoding = %u",
escapedSensorName, escapedInterfaceName,
data->detail, data->encoding)) != SNORT_SNPRINTF_SUCCESS)
{
/* XXX */
retval = 1;
goto exit_funct;
}
}
}
/* No check here */
Select(data->SQL_SELECT,data,(u_int32_t *)&data->sid);
if(data->sid == 0)
{
if( BeginTransaction(data) )
{
/* XXX */
FatalError("database [%s()]: Failed to Initialize transaction, bailing ... \n",
__FUNCTION__);
}
if(Insert(data->SQL_INSERT,data,1))
{
/* XXX */
FatalError("database Error inserting [%s] \n",data->SQL_INSERT);
}
if(CommitTransaction(data))
{
/* XXX */
ErrorMessage("ERROR database: [%s()]: Error commiting transaction \n",
__FUNCTION__);
setTransactionCallFail(&data->dbRH[data->dbtype_id]);
retval = 1;
goto exit_funct;
}
else
{
resetTransactionState(&data->dbRH[data->dbtype_id]);
}
if( Select(data->SQL_SELECT,data,(u_int32_t *)&data->sid))
{
/* XXX */
FatalError("database Error Executing [%s] \n",data->SQL_SELECT);
}
if(data->sid == 0)
{
ErrorMessage("ERROR database: Problem obtaining SENSOR ID (sid) from %s->sensor\n",
data->dbname);
FatalError("%s\n%s\n", FATAL_NO_SENSOR_1, FATAL_NO_SENSOR_2);
}
}
exit_funct:
if(escapedSensorName != NULL)
{
free(escapedSensorName);
escapedSensorName= NULL;
}
if(escapedInterfaceName != NULL)
{
free(escapedInterfaceName);
escapedInterfaceName = NULL;
}
if( escapedBPFFilter != NULL)
{
free(escapedBPFFilter);
escapedBPFFilter = NULL;
}
return retval;
}
void DatabaseInitFinalize(int unused, void *arg)
{
DatabaseData *data = (DatabaseData *)arg;
if ((data == NULL))
{
FatalError("database data uninitialized\n");
}
Connect(data);
if( (ConvertDefaultCache(barnyard2_conf,data)))
{
/* XXX */
FatalError("database [%s()], ConvertDefaultCache() Failed \n",
__FUNCTION__);
}
/* Get the versioning information for the DB schema */
if( (CheckDBVersion(data)))
{
/* XXX */
FatalError("database problems with schema version, bailing...\n");
}
if( (DatabasePluginInitializeSensor(data)))
{
FatalError("database Unable to initialize sensor \n");
}
if(SynchronizeEventId(data))
{
FatalError("database Encountered an error while trying to synchronize event_id, this is serious and we can't go any further, please investigate \n");
}
if(CacheSynchronize(data))
{
/* XXX */
FatalError("database [%s()]: CacheSynchronize() call failed ...\n",
__FUNCTION__);
return;
}
DatabasePluginPrintData(data);
SQL_Initialize(data);
return;
}
/*******************************************************************************
* Function: InitDatabaseData(char *)
*
* Purpose: Initialize the data structure for connecting to
* this database.
*
* Arguments: args => argument list
*
* Returns: Pointer to database structure
*
******************************************************************************/
DatabaseData *InitDatabaseData(char *args)
{
DatabaseData *data;
data = (DatabaseData *)SnortAlloc(sizeof(DatabaseData));
if(args == NULL)
{
ErrorMessage("ERROR database: you must supply arguments for database plugin\n");
DatabasePrintUsage();
FatalError("");
}
data->args = SnortStrdup(args);
return data;
}
/*******************************************************************************
* Function: ParseDatabaseArgs(char *)
*
* Purpose: Process the preprocessor arguements from the rules file and
* initialize the preprocessor's data struct.
*
* Arguments: args => argument list
*
* Returns: void function
*
******************************************************************************/
void ParseDatabaseArgs(DatabaseData *data)
{
char *dbarg;
char *a1;
char *type;
char *facility;
if(data->args == NULL)
{
ErrorMessage("ERROR database: you must supply arguments for database plugin\n");
DatabasePrintUsage();
FatalError("");
}
data->dbtype_id = DB_UNDEFINED;
data->sensor_name = NULL;
data->facility = NULL;
data->encoding = ENCODING_HEX;
data->detail = DETAIL_FULL;
data->ignore_bpf = 0;
data->use_ssl = 0;
facility = strtok(data->args, ", ");
if(facility != NULL)
{
if((!strncasecmp(facility,"log",3)) || (!strncasecmp(facility,"alert",5)))
data->facility = facility;
else
{
ErrorMessage("ERROR database: The first argument needs to be the logging facility\n");
DatabasePrintUsage();
FatalError("");
}
}
else
{
ErrorMessage("ERROR database: Invalid format for first argment\n");
DatabasePrintUsage();
FatalError("");
}
type = strtok(NULL, ", ");
if(type == NULL)
{
ErrorMessage("ERROR database: you must enter the database type in configuration "
"file as the second argument\n");
DatabasePrintUsage();
FatalError("");
}
#ifdef ENABLE_MYSQL
if(!strncasecmp(type,KEYWORD_MYSQL,strlen(KEYWORD_MYSQL)))
data->dbtype_id = DB_MYSQL;
#endif
#ifdef ENABLE_POSTGRESQL
if(!strncasecmp(type,KEYWORD_POSTGRESQL,strlen(KEYWORD_POSTGRESQL)))
data->dbtype_id = DB_POSTGRESQL;
#endif
#ifdef ENABLE_ODBC
if(!strncasecmp(type,KEYWORD_ODBC,strlen(KEYWORD_ODBC)))
data->dbtype_id = DB_ODBC;
#endif
#ifdef ENABLE_ORACLE
if(!strncasecmp(type,KEYWORD_ORACLE,strlen(KEYWORD_ORACLE)))
data->dbtype_id = DB_ORACLE;
#endif
#ifdef ENABLE_MSSQL
if(!strncasecmp(type,KEYWORD_MSSQL,strlen(KEYWORD_MSSQL)))
data->dbtype_id = DB_MSSQL;
#endif
if(data->dbtype_id == 0)
{
if ( !strncasecmp(type, KEYWORD_MYSQL, strlen(KEYWORD_MYSQL)) ||
!strncasecmp(type, KEYWORD_POSTGRESQL, strlen(KEYWORD_POSTGRESQL)) ||
!strncasecmp(type, KEYWORD_ODBC, strlen(KEYWORD_ODBC)) ||
!strncasecmp(type, KEYWORD_MSSQL, strlen(KEYWORD_MSSQL)) ||
!strncasecmp(type, KEYWORD_ORACLE, strlen(KEYWORD_ORACLE)) )
{
ErrorMessage("ERROR database: '%s' support is not compiled into this build of snort\n\n", type);
FatalError(FATAL_NO_SUPPORT_1, type, type, type, FATAL_NO_SUPPORT_2);
}
else
{
FatalError("database '%s' is an unknown database type. The supported\n"
" databases include: MySQL (mysql), PostgreSQL (postgresql),\n"
" ODBC (odbc), Oracle (oracle), and Microsoft SQL Server (mssql)\n",
type);
}
}
dbarg = strtok(NULL, " =");
while(dbarg != NULL)
{
a1 = NULL;
a1 = strtok(NULL, ", ");
if(!strncasecmp(dbarg,KEYWORD_HOST,strlen(KEYWORD_HOST)))
{
data->host = a1;
}
if(!strncasecmp(dbarg,KEYWORD_PORT,strlen(KEYWORD_PORT)))
{
data->port = a1;
}
if(!strncasecmp(dbarg,KEYWORD_USER,strlen(KEYWORD_USER)))
{
data->user = a1;
}
if(!strncasecmp(dbarg,KEYWORD_PASSWORD,strlen(KEYWORD_PASSWORD)))
{
data->password = a1;
}
if(!strncasecmp(dbarg,KEYWORD_DBNAME,strlen(KEYWORD_DBNAME)))
{
data->dbname = a1;
}
if(!strncasecmp(dbarg,KEYWORD_SENSORNAME,strlen(KEYWORD_SENSORNAME)))
{
data->sensor_name = a1;
}
if(!strncasecmp(dbarg,KEYWORD_ENCODING,strlen(KEYWORD_ENCODING)))
{
if(!strncasecmp(a1, KEYWORD_ENCODING_HEX, strlen(KEYWORD_ENCODING_HEX)))
{
data->encoding = ENCODING_HEX;
}
else if(!strncasecmp(a1, KEYWORD_ENCODING_BASE64, strlen(KEYWORD_ENCODING_BASE64)))
{
data->encoding = ENCODING_BASE64;
}
else if(!strncasecmp(a1, KEYWORD_ENCODING_ASCII, strlen(KEYWORD_ENCODING_ASCII)))
{
data->encoding = ENCODING_ASCII;
}
else
{
FatalError("database unknown (%s)", a1);
}
}
if(!strncasecmp(dbarg,KEYWORD_DETAIL,strlen(KEYWORD_DETAIL)))
{
if(!strncasecmp(a1, KEYWORD_DETAIL_FULL, strlen(KEYWORD_DETAIL_FULL)))
{
data->detail = DETAIL_FULL;
}
else if(!strncasecmp(a1, KEYWORD_DETAIL_FAST, strlen(KEYWORD_DETAIL_FAST)))
{
data->detail = DETAIL_FAST;
}
else
{
FatalError("database unknown detail level (%s)", a1);
}
}
if(!strncasecmp(dbarg,KEYWORD_IGNOREBPF,strlen(KEYWORD_IGNOREBPF)))
{
if(!strncasecmp(a1, KEYWORD_IGNOREBPF_NO, strlen(KEYWORD_IGNOREBPF_NO)) ||
!strncasecmp(a1, KEYWORD_IGNOREBPF_ZERO, strlen(KEYWORD_IGNOREBPF_ZERO)))
{
data->ignore_bpf = 0;
}
else if(!strncasecmp(a1, KEYWORD_IGNOREBPF_YES, strlen(KEYWORD_IGNOREBPF_YES)) ||
!strncasecmp(a1, KEYWORD_IGNOREBPF_ONE, strlen(KEYWORD_IGNOREBPF_ONE)))
{
data->ignore_bpf = 1;
}
else
{
FatalError("database unknown ignore_bpf argument (%s)", a1);
}
}
if(!strncasecmp(dbarg,KEYWORD_CONNECTION_LIMIT,strlen(KEYWORD_CONNECTION_LIMIT)))
{
data->dbRH[data->dbtype_id].dbConnectionLimit = strtoul(a1,NULL,10);
/* Might make a different option for it but for now lets consider
the threshold being the same as connectionlimit. */
data->dbRH[data->dbtype_id].transactionErrorThreshold = data->dbRH[data->dbtype_id].dbConnectionLimit;
}
if(!strncasecmp(dbarg,KEYWORD_RECONNECT_SLEEP_TIME,strlen(KEYWORD_RECONNECT_SLEEP_TIME)))
{
data->dbRH[data->dbtype_id].dbReconnectSleepTime.tv_sec = strtoul(a1,NULL,10);
}
#ifdef ENABLE_MYSQL
/* Option declared here should be forced to dbRH[DB_MYSQL] */
/* the if/elseif check order is important because the keywords for the */
/* ca and ca_path are very similar */
if(!strncasecmp(dbarg, KEYWORD_SSL_KEY, strlen(KEYWORD_SSL_KEY)))
{
data->dbRH[DB_MYSQL].ssl_key = a1;
data->use_ssl = 1;
}
else if(!strncasecmp(dbarg, KEYWORD_SSL_CERT, strlen(KEYWORD_SSL_CERT)))
{
data->dbRH[DB_MYSQL].ssl_cert = a1;
data->use_ssl = 1;
}
else if(!strncasecmp(dbarg, KEYWORD_SSL_CA_PATH, strlen(KEYWORD_SSL_CA_PATH)))
{
data->dbRH[DB_MYSQL].ssl_ca_path = a1;
data->use_ssl = 1;
}
else if(!strncasecmp(dbarg, KEYWORD_SSL_CA, strlen(KEYWORD_SSL_CA)))
{
data->dbRH[DB_MYSQL].ssl_ca = a1;
data->use_ssl = 1;
}
else if(!strncasecmp(dbarg, KEYWORD_SSL_CIPHER, strlen(KEYWORD_SSL_CIPHER)))
{
data->dbRH[DB_MYSQL].ssl_key = a1;
data->use_ssl = 1;
}
else if(!strncasecmp(dbarg, KEYWORD_MYSQL_RECONNECT, strlen(KEYWORD_MYSQL_RECONNECT)))
{
data->dbRH[DB_MYSQL].mysql_reconnect =1;
}
#endif
#ifdef ENABLE_POSTGRESQL
if(!strncasecmp(dbarg, KEYWORD_SSL_MODE, strlen(KEYWORD_SSL_MODE)))
{
if ( (!strncasecmp(a1, KEYWORD_SSL_MODE_DISABLE, strlen(KEYWORD_SSL_MODE_DISABLE))) ||
(!strncasecmp(a1, KEYWORD_SSL_MODE_ALLOW, strlen(KEYWORD_SSL_MODE_ALLOW))) ||
(!strncasecmp(a1, KEYWORD_SSL_MODE_PREFER, strlen(KEYWORD_SSL_MODE_PREFER))) ||
(!strncasecmp(a1, KEYWORD_SSL_MODE_REQUIRE, strlen(KEYWORD_SSL_MODE_REQUIRE))) )
{
data->dbRH[data->dbtype_id].ssl_mode = a1;
data->use_ssl = 1;
}
else
{
ErrorMessage("ERROR database: unknown ssl_mode argument (%s)", a1);
}
}
#endif
dbarg = strtok(NULL, "=");
}
if(data->dbtype_id == DB_ODBC)
{
/* Print Transaction Warning */
if(data->dbname == NULL)
{
ErrorMessage("database: no DSN was specified, unable to try to initialize ODBC connection. (use [dbname] parameter, in configuration file to set DSN)\n");
FatalError("");
}
else
{
LogMessage("database: will use DSN [%s] for ODBC Connection setup \n",
data->dbname);
}
if(data->host != NULL)
{
ErrorMessage("database: [host] [%s] will not be used, we will use infromation from the DSN [%s], make sure your setup is ok. \n",
data->host,
data->dbname);
}
if(data->user != NULL)
{
ErrorMessage("database: [user] [%s] will not be used, we will use infromation from the DSN [%s], make sure your setup is ok. \n",
data->user,
data->dbname);
}
if(data->port != NULL)
{
ErrorMessage("database: [port] [%s] will not be used, we will use infromation from the DSN [%s], make sure your setup is ok. \n",
data->port,
data->dbname);
}
}
else
{
if(data->dbname == NULL)
{
ErrorMessage("ERROR database: must enter database name in configuration file\n\n");
DatabasePrintUsage();
FatalError("");
}
else if(data->host == NULL)
{
ErrorMessage("ERROR database: must enter host in configuration file\n\n");
DatabasePrintUsage();
FatalError("");
}
}
if(data->dbRH[data->dbtype_id].dbConnectionLimit == 0)
{
LogMessage("INFO database: Defaulting Reconnect/Transaction Error limit to 10 \n");
data->dbRH[data->dbtype_id].dbConnectionLimit = 10;
/* Might make a different option for it but for now lets consider
the threshold being the same as connectionlimit. */
data->dbRH[data->dbtype_id].transactionErrorThreshold = data->dbRH[data->dbtype_id].dbConnectionLimit;
}
if(data->dbRH[data->dbtype_id].dbReconnectSleepTime.tv_sec == 0)
{
LogMessage("INFO database: Defaulting Reconnect sleep time to 5 second \n");
data->dbRH[data->dbtype_id].dbReconnectSleepTime.tv_sec = 5;
}
return;
}
/*
** This function will either insert a "new" signature, present in file and not in db and update
** the cache information (db_sig_id) or update an existing signature using its db_sig_id.
**
*/
u_int32_t dbSignatureInformationUpdate(DatabaseData *data,cacheSignatureObj *iUpdateSig)
{
u_int32_t db_sig_id = 0;
if( (data == NULL) ||
(iUpdateSig == NULL))
{
/* XXX */
return 1;
}
DatabaseCleanSelect(data);
DatabaseCleanInsert(data);
if( SnortSnprintf(data->SQL_SELECT,data->SQL_SELECT_SIZE,
SQL_SELECT_SPECIFIC_SIGNATURE,
iUpdateSig->obj.sid,
iUpdateSig->obj.gid,
iUpdateSig->obj.rev,
iUpdateSig->obj.class_id,
iUpdateSig->obj.priority_id,
iUpdateSig->obj.message))
{
/* XXX */
LogMessage("ERROR database: calling SnortSnprintf() on data->SQL_SELECT in [%s()] \n",
__FUNCTION__);
return 1;
}
if(iUpdateSig->flag & CACHE_BOTH ||
iUpdateSig->flag & CACHE_DATABASE_ONLY)
{
if( SnortSnprintf(data->SQL_INSERT,data->SQL_INSERT_SIZE,
SQL_UPDATE_SPECIFIC_SIGNATURE,
iUpdateSig->obj.class_id,
iUpdateSig->obj.priority_id,
iUpdateSig->obj.rev,
iUpdateSig->obj.db_id))
{
/* XXX */
LogMessage("ERROR database: calling SnortSnprintf() on data->SQL_INSERT in [%s()] \n",
__FUNCTION__);
return 1;
}
}
else
{
if( SnortSnprintf(data->SQL_INSERT,data->SQL_INSERT_SIZE,
SQL_INSERT_SIGNATURE,
iUpdateSig->obj.sid,
iUpdateSig->obj.gid,
iUpdateSig->obj.rev,
iUpdateSig->obj.class_id,
iUpdateSig->obj.priority_id,
iUpdateSig->obj.message))
{
/* XXX */
LogMessage("ERROR database: calling SnortSnprintf() on data->SQL_INSERT in [%s()] \n",
__FUNCTION__);
return 1;
}
}
#if DEBUG
DEBUG_WRAP(DebugMessage(DB_DEBUG,"[%s()] Issuing signature update [%s]\n",
__FUNCTION__,
data->SQL_INSERT));
#endif
if(Insert(data->SQL_INSERT,data,1))
{
/* XXX */
LogMessage("ERROR database: calling Insert() in [%s()] \n",
__FUNCTION__);
return 1;
}
if(Select(data->SQL_SELECT,data,(u_int32_t *)&db_sig_id))
{
/* XXX */
LogMessage("ERROR database: calling Select() in [%s()] \n",
__FUNCTION__);
return 1;
}
if(iUpdateSig->flag & CACHE_INTERNAL_ONLY)
{
iUpdateSig->flag ^=(CACHE_INTERNAL_ONLY | CACHE_BOTH);
iUpdateSig->obj.db_id = db_sig_id;
}
else if(iUpdateSig->flag & CACHE_BOTH ||
iUpdateSig->flag & CACHE_DATABASE_ONLY)
{
if(db_sig_id != iUpdateSig->obj.db_id)
{
/* XXX */
LogMessage("ERROR database: Returned signature_id [%u] is not equal to updated signature_id [%u] in [%s()] \n",
db_sig_id,
iUpdateSig->obj.db_id,
__FUNCTION__);
return 1;
}
}
return 0;
}
/* NOTE: -elz this function need to be broken up.. */
int dbProcessSignatureInformation(DatabaseData *data,void *event, u_int32_t event_type,
u_int32_t *psig_id)
{
cacheSignatureObj unInitSig;
dbSignatureObj sigInsertObj= {0};
u_int32_t x =0;
u_int32_t db_classification_id = 0;
u_int32_t sigMatchCount = 0;
u_int32_t sid = 0;
u_int32_t gid = 0;
u_int32_t revision = 0;
u_int32_t priority = 0;
u_int32_t classification = 0;
u_int32_t sigMsgLen = 0;
u_int8_t reuseSigMsg = 0;
if( (data == NULL) ||
(event == NULL) ||
(psig_id == NULL))
{
/* XXX */
return 1;
}
memset(&unInitSig,'\0',sizeof(cacheSignatureObj));
*psig_id = 0;
sid = ntohl(((Unified2EventCommon *)event)->signature_id);
gid = ntohl(((Unified2EventCommon *)event)->generator_id);
revision = ntohl(((Unified2EventCommon *)event)->signature_revision);
priority = ntohl(((Unified2EventCommon *)event)->priority_id);
classification = ntohl(((Unified2EventCommon *)event)->classification_id);
/* NOTE: elz
For sanity purpose the sig_class table SHOULD have internal classification id to prevent possible
miss classification tagging ... but this is not happening with the old schema.
*/
#if DEBUG
DEBUG_WRAP(DebugMessage(DB_DEBUG,"[%s()], Classification cachelookup [class_id: %u]\n",
__FUNCTION__,
classification));
#endif
db_classification_id = cacheEventClassificationLookup(data->mc.cacheClassificationHead,classification);
/*
This function comes with a little twist where it return the number of matching couple for
gid sid up to a maximum of 255 (arbitrary defined) this is a static buffer and it is cleaned every call
from there if its traversed and compared with revision and priority and classification
if one or both differs its reported and inserted ....
*/
#if DEBUG
DEBUG_WRAP(DebugMessage(DB_DEBUG,"[%s()], Signature cachelookup [gid: %u] [sid: %u]\n",
__FUNCTION__,
gid,
sid));
#endif
if( (sigMatchCount = cacheEventSignatureLookup(data->mc.cacheSignatureHead,
data->mc.plgSigCompare,
gid,sid)) > 0 )
{
for(x = 0 ; x < sigMatchCount ; x++)
{
if( (data->mc.plgSigCompare[x].cacheSigObj->obj.rev == revision) &&
(data->mc.plgSigCompare[x].cacheSigObj->obj.class_id == db_classification_id) &&
(data->mc.plgSigCompare[x].cacheSigObj->obj.priority_id == priority))
{
/* Added for bugcheck */
assert( data->mc.plgSigCompare[x].cacheSigObj->obj.db_id != 0);
*psig_id = data->mc.plgSigCompare[x].cacheSigObj->obj.db_id;
return 0;
}
/* If we have an "uninitialized signature save it */
if( data->mc.plgSigCompare[x].cacheSigObj->obj.rev == 0 ||
data->mc.plgSigCompare[x].cacheSigObj->obj.rev < revision)
{
memcpy(&unInitSig,data->mc.plgSigCompare[x].cacheSigObj,sizeof(cacheSignatureObj));
/*
** We assume that we have the same signature, but with a smaller revision
** set the unInitSig db_id to 0 for post processing if we do not find a matching
** signature, and get the lastest revision
*/
if( (data->mc.plgSigCompare[x].cacheSigObj->obj.rev < revision) ||
(data->mc.plgSigCompare[x].cacheSigObj->obj.rev > unInitSig.obj.rev))
{
unInitSig.obj.db_id = 0;
}
}
}
}
/*
This shouldn't be needed since unitialized signature are not inserted anymore, thus preventing the need for update
if(unInitSig.obj.db_id != 0)
{
#if DEBUG
DEBUG_WRAP(DebugMessage(DB_DEBUG,"[%s()], [%u] signatures where found in cache for [gid: %u] [sid: %u] but non matched\n"
"updating database [db_sig_id: %u] with [rev: 0] to [rev: %u] \n",
__FUNCTION__,
sigMatchCount,
gid,
sid,
unInitSig.obj.db_id,
revision));
#endif
unInitSig.obj.rev = revision;
unInitSig.obj.class_id = db_classification_id;
unInitSig.obj.priority_id = priority;
if( (dbSignatureInformationUpdate(data,&unInitSig)))
{
LogMessage("[%s()] Line[%u], call to dbSignatureInformationUpdate failed for : \n"
"[gid :%u] [sid: %u] [upd_rev: %u] [upd class: %u] [upd pri %u]\n",
__FUNCTION__,
__LINE__,
gid, \
sid,
revision,
db_classification_id,
priority);
return 1;
}
assert( unInitSig.obj.db_id != 0);
*psig_id = unInitSig.obj.db_id;
return 0;
}
*/
/*
To avoid possible collision with an older barnyard process or
avoid signature insertion race condition we will look in the
database if the signature exist, if it does, we will insert it in
cache else we will insert in db and cache
*/
sigInsertObj.sid = sid;
sigInsertObj.gid = gid;
sigInsertObj.rev = revision;
sigInsertObj.class_id = db_classification_id;
sigInsertObj.priority_id = priority;
if( SignatureLookupDatabase(data,&sigInsertObj))
{
if(unInitSig.obj.sid != 0 && unInitSig.obj.gid != 0)
{
sigMsgLen = strlen(unInitSig.obj.message);
if( (sigMsgLen > 1) &&
(sigMsgLen < SIG_MSG_LEN))
{
reuseSigMsg = 1;
}
}
if(reuseSigMsg)
{
/* The signature was not found we will have to insert it */
LogMessage("INFO [%s()]: [Event: %u] with [gid: %u] [sid: %u] [rev: %u] [classification: %u] [priority: %u] Signature Message -> \"[%s]\"\n"
"\t was not found in barnyard2 signature cache, this could mean its is the first time the signature is processed, and will be inserted\n"
"\t in the database with the above information, this message should only be printed once for each signature that is not present in the database\n"
"\t The new inserted signature will not have its information present in the sig_reference table,it should be present on restart\n"
"\t if the information is present in the sid-msg.map file. \n"
"\t You can allways update the message via a SQL query if you want it to be displayed correctly by your favorite interface\n\n",
__FUNCTION__,
ntohl(((Unified2EventCommon *)event)->event_id),
gid,
sid,
revision,
db_classification_id,
priority,
unInitSig.obj.message);
if( SnortSnprintf(sigInsertObj.message,SIG_MSG_LEN,"%s",
unInitSig.obj.message))
{
/* XXX */
return 1;
}
}
else
{
/* The signature does not exist we will have to insert it */
LogMessage("INFO [%s()]: [Event: %u] with [gid: %u] [sid: %u] [rev: %u] [classification: %u] [priority: %u]\n"
"\t was not found in barnyard2 signature cache, this could lead to display inconsistency.\n"
"\t To prevent this warning, make sure that your sid-msg.map and gen-msg.map file are up to date with the snort process logging to the spool file.\n"
"\t The new inserted signature will not have its information present in the sig_reference table. \n"
"\t Note that the message inserted in the signature table will be snort default message \"Snort Alert [gid:sid:revision]\" \n"
"\t You can allways update the message via a SQL query if you want it to be displayed correctly by your favorite interface\n\n",
__FUNCTION__,
ntohl(((Unified2EventCommon *)event)->event_id),
gid,
sid,
revision,
db_classification_id,
priority);
if( SnortSnprintf(sigInsertObj.message,SIG_MSG_LEN,"Snort Alert [%u:%u:%u]",
gid,sid,revision))
{
/* XXX */
return 1;
}
}
if( (SignatureCacheInsertObj(&sigInsertObj,&data->mc,0)))
{
/* XXX */
LogMessage("[%s()]: ERROR inserting object in the cache list .... \n",
__FUNCTION__);
goto func_err;
}
/*
There is some little overhead traversing the list once
the insertion is done on the HEAD so
unless you run 1M rules and still there it should
complete in just a few more jiffies, also its better this way
than to query the database everytime isin't.
*/
if(SignaturePopulateDatabase(data,data->mc.cacheSignatureHead,1))
{
/* XXX */
LogMessage("[%s()]: ERROR inserting new signature \n",
__FUNCTION__);
goto func_err;
}
}
else
{
if( (SignatureCacheInsertObj(&sigInsertObj,&data->mc,1)))
{
/* XXX */
LogMessage("[%s()]: ERROR inserting object in the cache list .... \n",
__FUNCTION__);
goto func_err;
}
}
/* Added for bugcheck */
assert( data->mc.cacheSignatureHead->obj.db_id != 0);
*psig_id = data->mc.cacheSignatureHead->obj.db_id;
return 0;
func_err:
return 1;
}
int dbProcessEventInformation(DatabaseData *data,Packet *p,
void *event,
u_int32_t event_type,
u_int32_t i_sig_id)
{
char *SQLQueryPtr = NULL;
int i = 0;
if( (data == NULL) ||
(p == NULL) ||
(event == NULL))
{
/* XXX */
/* Mabey move to debug... */
LogMessage("[%s()]: Bailing, Invoked with DatabaseData *[0x%x] Packet *[0x%x] Event(void) *[0x%x] \n",
__FUNCTION__,
data,
p,
event);
return 1;
}
/*
CHECKME: -elz We need to get this logic sorted out since event shouldn't be null
theorically and event time should be priorized
*/
/* Generate a default-formatted timestamp now */
memset(data->timestampHolder,'\0',SMALLBUFFER);
if(event != NULL)
{
if( (GetTimestampByComponent_STATIC(
ntohl(((Unified2EventCommon *)event)->event_second),
ntohl(((Unified2EventCommon *)event)->event_microsecond),
data->tz,data->timestampHolder)))
{
/* XXX */
return 1;
}
}
else if(p != NULL)
{
if( (GetTimestampByStruct_STATIC((struct timeval *) &p->pkth->ts,
data->tz,data->timestampHolder)))
{
/* XXX */
return 1;
}
}
else
{
if(GetCurrentTimestamp_STATIC(data->timestampHolder))
{
/* XXX */
return 1;
}
}
/* Some timestring comments comments */
/* SQL Server uses a date format which is slightly
* different from the ISO-8601 standard generated
* by GetTimestamp() and GetCurrentTimestamp(). We
* need to convert from the ISO-8601 format of:
* "1998-01-25 23:59:59+14316557"
* to the SQL Server format of:
* "1998-01-25 23:59:59.143"
*/
/* Oracle (everything before 9i) does not support
* date information smaller than 1 second.
* To go along with the TO_DATE() Oracle function
* below, this was written to strip out all the
* excess information. (everything beyond a second)
* Use the Oracle format of:
* "1998-01-25 23:59:59"
*/
/* MySql does not support date information smaller than
* 1 second. This was written to strip out all the
* excess information. (everything beyond a second)
* Use the MySql format of:
* "2005-12-23 22:37:16"
*/
/* ODBC defines escape sequences for date data.
* These escape sequences are of the format:
* {literal-type 'value'}
* The Timestamp (ts) escape sequence handles
* date/time values of the format:
* yyyy-mm-dd hh:mm:ss[.f...]
* where the number of digits to the right of the
* decimal point in a time or timestamp interval
* literal containing a seconds component is
* dependent on the seconds precision, as contained
* in the SQL_DESC_PRECISION descriptor field. (For
* more information, see function SQLSetDescField.)
*
* The number of decimal places within the fraction
* of a second is database dependant. I wasn't able
* to easily determine the granularity of this
* value using SQL_DESC_PRECISION, so choosing to
* simply discard the fractional part.
*/
/* From Posgres Documentation
* For timestamp with time zone, the internally stored
* value is always in UTC (GMT). An input value that has
* an explicit time zone specified is converted to UTC
* using the appropriate offset for that time zone. If no
* time zone is stated in the input string, then it is assumed
* to be in the time zone indicated by the system's TimeZone
* parameter, and is converted to UTC using the offset for
* the TimeZone zone
*/
/* Some timestring comments comments */
/*
COMMENT: -elz
The new schema will log timestamp in UTC,
no need for resolve time to be logged as a string literal,
this should be handled by UI's.
*/
if( (SQLQueryPtr=SQL_GetNextQuery(data)) == NULL)
{
goto bad_query;
}
switch(data->dbtype_id)
{
case DB_MSSQL:
case DB_MYSQL:
case DB_ORACLE:
case DB_ODBC:
if(strlen(data->timestampHolder) > 20)
{
data->timestampHolder[19] = '\0';
}
break;
case DB_POSTGRESQL:
default:
if(strlen(data->timestampHolder) > 24)
{
data->timestampHolder[23] = '\0';
}
break;
}
switch(data->dbtype_id)
{
case DB_ORACLE:
if((data->DBschema_version >= 105) )
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"event (sid,cid,signature,timestamp) "
"VALUES (%u, %u, %u, TO_DATE('%s', 'YYYY-MM-DD HH24:MI:SS'));",
data->sid,
data->cid,
i_sig_id,
data->timestampHolder)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
else
{
/*
COMMENT: -elz
I just hate useless duplication and this
dosent break anything so just go down please
*/
goto GenericEVENTQUERYJMP;
}
break;
/* -elz: ODBC with {ts ....} string for timestamp!? nha...
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"event (sid,cid,signature,timestamp) "
"VALUES (%u, %u, %u, {ts '%s'})",
data->sid,
data->cid,
i_sig_id,
data->timestampHolder)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
break;
*/
case DB_MSSQL:
case DB_MYSQL:
case DB_POSTGRESQL:
case DB_ODBC:
default:
GenericEVENTQUERYJMP:
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"event (sid,cid,signature,timestamp) "
"VALUES (%u, %u, %u, '%s');",
data->sid,
data->cid,
i_sig_id,
data->timestampHolder)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
break;
}
/* We do not log fragments! They are assumed to be handled
by the fragment reassembly pre-processor */
if(p != NULL)
{
if((!p->frag_flag) && (IPH_IS_VALID(p)))
{
switch(GET_IPH_PROTO(p))
{
case IPPROTO_ICMP:
if( (SQLQueryPtr=SQL_GetNextQuery(data)) == NULL)
{
goto bad_query;
}
/* IPPROTO_ICMP */
if(p->icmph)
{
/*** Build a query for the ICMP Header ***/
if(data->detail)
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"icmphdr (sid, cid, icmp_type, icmp_code, icmp_csum, icmp_id, icmp_seq) "
"VALUES (%u,%u,%u,%u,%u,%u,%u);",
data->sid,
data->cid,
p->icmph->type,
p->icmph->code,
ntohs(p->icmph->csum),
ntohs(p->icmph->s_icmp_id),
ntohs(p->icmph->s_icmp_seq))) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
else
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"icmphdr (sid, cid, icmp_type, icmp_code) "
"VALUES (%u,%u,%u,%u);",
data->sid,
data->cid,
p->icmph->type,
p->icmph->code)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
}
else
{
LogMessage("[%s()], unable to build query, IP header tell's us its an ICMP packet but "
"there is not icmp header in the decoded packet ... \n",
__FUNCTION__);
goto bad_query;
}
break;
/* IPPROTO_ICMP */
/* IPPROTO_TCP */
case IPPROTO_TCP:
if( (SQLQueryPtr=SQL_GetNextQuery(data)) == NULL)
{
goto bad_query;
}
/*** Build a query for the TCP Header ***/
if(data->detail)
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"tcphdr (sid, cid, tcp_sport, tcp_dport, "
"tcp_seq, tcp_ack, tcp_off, tcp_res, "
"tcp_flags, tcp_win, tcp_csum, tcp_urp) "
"VALUES (%u,%u,%u,%u,%lu,%lu,%u,%u,%u,%u,%u,%u);",
data->sid,
data->cid,
ntohs(p->tcph->th_sport),
ntohs(p->tcph->th_dport),
(u_long)ntohl(p->tcph->th_seq),
(u_long)ntohl(p->tcph->th_ack),
TCP_OFFSET(p->tcph),
TCP_X2(p->tcph),
p->tcph->th_flags,
ntohs(p->tcph->th_win),
ntohs(p->tcph->th_sum),
ntohs(p->tcph->th_urp))) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
else
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"tcphdr (sid,cid,tcp_sport,tcp_dport,tcp_flags) "
"VALUES (%u,%u,%u,%u,%u);",
data->sid,
data->cid,
ntohs(p->tcph->th_sport),
ntohs(p->tcph->th_dport),
p->tcph->th_flags)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
if(data->detail)
{
/*** Build the query for TCP Options ***/
for(i=0; i < (int)(p->tcp_option_count); i++)
{
if( p->tcp_options[i].len > 0)
{
if( (SQLQueryPtr=SQL_GetNextQuery(data)) == NULL)
{
goto bad_query;
}
if((data->encoding == ENCODING_HEX) || (data->encoding == ENCODING_ASCII))
{
//packet_data = fasthex(p->tcp_options[i].data, p->tcp_options[i].len);
if( fasthex_STATIC(p->tcp_options[i].data, p->tcp_options[i].len,data->PacketData))
{
/* XXX */
goto bad_query;
}
}
else
{
//packet_data = base64(p->tcp_options[i].data, p->tcp_options[i].len);
if( base64_STATIC(p->tcp_options[i].data, p->tcp_options[i].len,data->PacketData))
{
/* XXX */
goto bad_query;
}
}
if(data->dbtype_id == DB_ORACLE)
{
/* Oracle field BLOB type case. We append unescaped
* opt_data data after query, which later in Insert()
* will be cut off and uploaded with OCIBindByPos().
*/
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"opt (sid,cid,optid,opt_proto,opt_code,opt_len,opt_data) "
"VALUES (%u,%u,%u,%u,%u,%u,:1);|%s",
data->sid,
data->cid,
i,
6,
p->tcp_options[i].code,
p->tcp_options[i].len,
//packet_data)) != SNORT_SNPRINTF_SUCCESS)
data->PacketData)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
else
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"opt (sid,cid,optid,opt_proto,opt_code,opt_len,opt_data) "
"VALUES (%u,%u,%u,%u,%u,%u,'%s');",
data->sid,
data->cid,
i,
6,
p->tcp_options[i].code,
p->tcp_options[i].len,
//packet_data)) != SNORT_SNPRINTF_SUCCESS)
data->PacketData)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
}
}
}
break;
/* IPPROTO_TCP */
/* IPPROTO_UDP */
case IPPROTO_UDP:
/*** Build the query for the UDP Header ***/
if( (SQLQueryPtr=SQL_GetNextQuery(data)) == NULL)
{
goto bad_query;
}
if(data->detail)
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"udphdr (sid, cid, udp_sport, udp_dport, udp_len, udp_csum) "
"VALUES (%u, %u, %u, %u, %u, %u);",
data->sid,
data->cid,
ntohs(p->udph->uh_sport),
ntohs(p->udph->uh_dport),
ntohs(p->udph->uh_len),
ntohs(p->udph->uh_chk))) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
else
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"udphdr (sid, cid, udp_sport, udp_dport) "
"VALUES (%u, %u, %u, %u);",
data->sid,
data->cid,
ntohs(p->udph->uh_sport),
ntohs(p->udph->uh_dport))) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
break;
/* IPPROTO_UDP */
/* DEFAULT */
default:
/* Do nothing ... */
break;
/* DEFAULT */
}
/*** Build the query for the IP Header ***/
if(p->iph)
{
if( (SQLQueryPtr=SQL_GetNextQuery(data)) == NULL)
{
goto bad_query;
}
if(data->detail)
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"iphdr (sid, cid, ip_src, ip_dst, ip_ver, ip_hlen, "
"ip_tos, ip_len, ip_id, ip_flags, ip_off,"
"ip_ttl, ip_proto, ip_csum) "
"VALUES (%u,%u,%lu,%lu,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u);",
data->sid,
data->cid,
(u_long)ntohl(p->iph->ip_src.s_addr),
(u_long)ntohl(p->iph->ip_dst.s_addr),
IP_VER(p->iph),
IP_HLEN(p->iph),
p->iph->ip_tos,
ntohs(p->iph->ip_len),
ntohs(p->iph->ip_id),
p->frag_flag,
ntohs(p->frag_offset),
p->iph->ip_ttl,
p->iph->ip_proto,
ntohs(p->iph->ip_csum))) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
else
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"iphdr (sid, cid, ip_src, ip_dst, ip_proto) "
"VALUES (%u,%u,%lu,%lu,%u);",
data->sid,
data->cid,
(u_long)ntohl(p->iph->ip_src.s_addr),
(u_long)ntohl(p->iph->ip_dst.s_addr),
GET_IPH_PROTO(p))) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
/*** Build querys for the IP Options ***/
if(data->detail)
{
for(i=0 ; i < (int)(p->ip_option_count); i++)
{
if(&p->ip_options[i])
{
if( (SQLQueryPtr=SQL_GetNextQuery(data)) == NULL)
{
goto bad_query;
}
if((data->encoding == ENCODING_HEX) ||
(data->encoding == ENCODING_ASCII))
{
//packet_data = fasthex(p->ip_options[i].data, p->ip_options[i].len);
if( fasthex_STATIC(p->ip_options[i].data, p->ip_options[i].len,data->PacketData))
{
/* XXX */
goto bad_query;
}
}
else
{
//packet_data = base64(p->ip_options[i].data, p->ip_options[i].len);
if( base64_STATIC(p->ip_options[i].data, p->ip_options[i].len,data->PacketData))
{
/* XXX */
goto bad_query;
}
}
if(data->dbtype_id == DB_ORACLE)
{
/* Oracle field BLOB type case. We append unescaped
* opt_data data after query, which later in Insert()
* will be cut off and uploaded with OCIBindByPos().
*/
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"opt (sid,cid,optid,opt_proto,opt_code,opt_len,opt_data) "
"VALUES (%u,%u,%u,%u,%u,%u,:1);|%s",
data->sid,
data->cid,
i,
0,
p->ip_options[i].code,
p->ip_options[i].len,
//packet_data)) != SNORT_SNPRINTF_SUCCESS)
data->PacketData)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
else
{
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"opt (sid,cid,optid,opt_proto,opt_code,opt_len,opt_data) "
"VALUES (%u,%u,%u,%u,%u,%u,'%s');",
data->sid,
data->cid,
i,
0,
p->ip_options[i].code,
p->ip_options[i].len,
//packet_data)) != SNORT_SNPRINTF_SUCCESS)
data->PacketData)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
}
}
}
}
}
/*** Build query for the payload ***/
if ( p->data )
{
if(data->detail)
{
if(p->dsize)
{
if( (SQLQueryPtr=SQL_GetNextQuery(data)) == NULL)
{
goto bad_query;
}
if(data->encoding == ENCODING_BASE64)
{
//packet_data_not_escaped = base64(p->data, p->dsize);
if(base64_STATIC(p->data,p->dsize,data->PacketDataNotEscaped))
{
/* XXX */
goto bad_query;
}
}
else if(data->encoding == ENCODING_ASCII)
{
//packet_data_not_escaped = ascii(p->data, p->dsize);
if(ascii_STATIC(p->data, p->dsize,data->PacketDataNotEscaped))
{
/* XXX */
goto bad_query;
}
}
else
{
//packet_data_not_escaped = fasthex(p->data, p->dsize);
if( (fasthex_STATIC(p->data, p->dsize,data->PacketDataNotEscaped)))
{
/* XXX */
goto bad_query;
}
}
//packet_data = snort_escape_string(packet_data_not_escaped, data);
if( snort_escape_string_STATIC(data->PacketDataNotEscaped,strlen(data->PacketDataNotEscaped)+1,data))
{
/* XXX */
goto bad_query;
}
switch(data->dbtype_id)
{
case DB_ORACLE:
/* Oracle field BLOB type case. We append unescaped
* packet_payload data after query, which later in Insert()
* will be cut off and uploaded with OCIBindByPos().
*/
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"data (sid,cid,data_payload) "
"VALUES (%u,%u,:1);|%s",
data->sid,
data->cid,
//packet_data_not_escaped)) != SNORT_SNPRINTF_SUCCESS)
data->PacketDataNotEscaped)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
break;
default:
if( (SnortSnprintf(SQLQueryPtr, MAX_QUERY_LENGTH,
"INSERT INTO "
"data (sid,cid,data_payload) "
"VALUES (%u,%u,'%s');",
data->sid,
data->cid,
//packet_data)) != SNORT_SNPRINTF_SUCCESS)
data->sanitize_buffer)) != SNORT_SNPRINTF_SUCCESS)
{
goto bad_query;
}
break;
}
}
}
}
}
}
return 0;
bad_query:
setTransactionCallFail(&data->dbRH[data->dbtype_id]);
return 1;
}
/*******************************************************************************
* Function: Database(Packet *p, void *event, uint32_t event_type, void *arg)
*
* Purpose: Insert data into the database
*
* Arguments: p => pointer to the current packet data struct
* msg => pointer to the signature message
*
* Returns: void function
*
******************************************************************************/
void Database(Packet *p, void *event, uint32_t event_type, void *arg)
{
DatabaseData *data = (DatabaseData *)arg;
char *CurrentQuery = NULL;
u_int32_t sig_id = 0;
u_int32_t itr = 0;
u_int32_t SQLMaxQuery = 0;
if(data == NULL)
{
FatalError("database [%s()]: Called with a NULL DatabaseData Argument, can't process \n",
__FUNCTION__);
}
if( event == NULL || p == NULL)
{
LogMessage("WARNING database [%s()]: Called with Event[0x%x] Event Type [%u] (P)acket [0x%x], information has not been outputed. \n",
__FUNCTION__,
event,
event_type,
p);
return;
}
/*
Check for invalid revision eg: rev==0 when people write their own testing signature and
do not set a revision, in our context we will not log it to the database
and print a informative messsage
*/
u_int32_t sid = 0;
u_int32_t gid = 0;
u_int32_t revision = 0;
u_int32_t event_id = 0;
u_int32_t event_second = 0;
u_int32_t event_microsecond = 0;
sid = ntohl(((Unified2EventCommon *)event)->signature_id);
gid = ntohl(((Unified2EventCommon *)event)->generator_id);
revision = ntohl(((Unified2EventCommon *)event)->signature_revision);
event_id = ntohl(((Unified2EventCommon *)event)->event_id);
event_second = ntohl(((Unified2EventCommon *)event)->event_second);
event_microsecond = ntohl(((Unified2EventCommon *)event)->event_microsecond);
if( (gid == 1) &&
(revision == 0))
{
LogMessage("INFO: Current event with event_id [%u] Event Second:Microsecond [%u:%u] and signature id of [%u] was logged with a revision of [%u]\n"
" Make sure you verify your triggering rule body so it include the snort keyword \"rev:xxx;\" Where xxx is greater than 0 \n"
">>>>>>The event has not been logged to the database<<<<<<\n",
event_id,
event_second,
event_microsecond,
sid,
revision);
return;
}
/*
This has been refactored to simplify the workflow of the function
We separate the legacy signature entry code and the event entry code
*/
/* Point where transaction rollback */
TransacRollback:
if(checkTransactionState(&data->dbRH[data->dbtype_id]) &&
checkTransactionCall(&data->dbRH[data->dbtype_id]))
{
if(RollbackTransaction(data))
{
/* XXX */
FatalError("database Unable to rollback transaction in [%s()]\n",
__FUNCTION__);
}
resetTransactionState(&data->dbRH[data->dbtype_id]);
}
if( BeginTransaction(data) )
{
/* XXX */
FatalError("database [%s()]: Failed to Initialize transaction, bailing ... \n",
__FUNCTION__);
}
if( dbProcessSignatureInformation(data,event,event_type,&sig_id))
{
/* XXX */
setTransactionCallFail(&data->dbRH[data->dbtype_id]);
FatalError("[dbProcessSignatureInformation()]: Failed, stoping processing \n");
}
if( dbProcessEventInformation(data,p,event,event_type,sig_id))
{
/* XXX */
setTransactionCallFail(&data->dbRH[data->dbtype_id]);
FatalError("[dbProcessEventInformation()]: Failed, stoping processing \n");
}
if( (SQLMaxQuery = SQL_GetMaxQuery(data)))
{
itr = 0;
for(itr = 0 ; itr < SQLMaxQuery; itr++)
{
CurrentQuery = NULL;
if( (CurrentQuery = SQL_GetQueryByPos(data,itr)) == NULL)
{
/* XXX */
goto bad_query;
}
if (Insert(CurrentQuery,data,1))
{
setTransactionCallFail(&data->dbRH[data->dbtype_id]);
ErrorMessage("[%s()]: Insertion of Query [%s] failed\n",
__FUNCTION__,
CurrentQuery);
goto bad_query;
break;
}
}
}
if(CommitTransaction(data))
{
/* XXX */
ErrorMessage("ERROR database: [%s()]: Error commiting transaction \n",
__FUNCTION__);
setTransactionCallFail(&data->dbRH[data->dbtype_id]);
goto bad_query;
}
else
{
resetTransactionState(&data->dbRH[data->dbtype_id]);
}
/* Clean the query */
SQL_Cleanup(data);
/* Increment the cid*/
data->cid++;
//LogMessage("Inserted a new event \n");
/* Normal Exit Path */
return;
bad_query:
if( (SQLMaxQuery = SQL_GetMaxQuery(data)))
{
LogMessage("WARNING database: [%s()] Failed transaction with current query transaction \n ",
__FUNCTION__);
itr = 0;
for(itr = 0 ; itr < SQLMaxQuery; itr++)
{
CurrentQuery = NULL;
if( (CurrentQuery = SQL_GetQueryByPos(data,itr)) == NULL)
{
/* XXX */
FatalError("database [%s()]: Failed to execute SQL_GetQueryByPos() in bad_query state, exiting \n",
__FUNCTION__);
}
LogMessage("WARNING database: Failed Query Position [%d] Failed Query Body [%s] \n",
itr+1,
CurrentQuery);
}
LogMessage("WARNING database [%s()]: End of failed transaction block \n",
__FUNCTION__);
}
SQL_Cleanup(data);
if( checkTransactionCall(&data->dbRH[data->dbtype_id]))
{
goto TransacRollback;
}
return;
}
/* Some of the code in this function is from the
mysql_real_escape_string() function distributed with mysql.
Those portions of this function remain
Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
We needed a more general case that was not MySQL specific so there
were small modifications made to the mysql_real_escape_string()
function. */
char * snort_escape_string(char * from, DatabaseData * data)
{
char * to;
char * to_start;
char * end;
int from_length;
from_length = (int)strlen(from);
to = (char *)SnortAlloc(strlen(from) * 2 + 1);
to_start = to;
#ifdef ENABLE_ORACLE
if (data->dbtype_id == DB_ORACLE)
{
for (end=from+from_length; from != end; from++)
{
switch(*from)
{
case '\'': /* ' --> '' */
*to++= '\'';
*to++= '\'';
break;
case '\032': /* Ctrl-Z (Win32 EOF) --> \\Z */
*to++= '\\'; /* This gives problems on Win32 */
*to++= 'Z';
break;
default: /* copy character directly */
*to++= *from;
}
}
}
else
#endif
#ifdef ENABLE_MSSQL
if (data->dbtype_id == DB_MSSQL)
{
for (end=from+from_length; from != end; from++)
{
switch(*from)
{
case '\'': /* ' --> '' */
*to++= '\'';
*to++= '\'';
break;
default: /* copy character directly */
*to++= *from;
}
}
}
else
#endif
/* Historically these were together in a common "else".
* Keeping it that way until somebody complains...
*/
#if defined(ENABLE_MYSQL) || defined(ENABLE_POSTGRESQL)
if (data->dbtype_id == DB_MYSQL ||
data->dbtype_id == DB_POSTGRESQL)
{
for(end=from+from_length; from != end; from++)
{
switch(*from)
{
/*
* Only need to escape '%' and '_' characters
* when querying a SELECT...LIKE, which never
* occurs in Snort. Excluding these checks
* for that reason.
case '%': ** % --> \% **
*to++= '\\';
*to++= '%';
break;
case '_': ** _ --> \_ **
*to++= '\\';
*to++= '_';
break;
*/
case 0: /* NULL --> \\0 (probably never encountered due to strlen() above) */
*to++= '\\'; /* Must be escaped for 'mysql' */
*to++= '0';
break;
case '\n': /* \n --> \\n */
*to++= '\\'; /* Must be escaped for logs */
*to++= 'n';
break;
case '\r': /* \r --> \\r */
*to++= '\\';
*to++= 'r';
break;
case '\t': /* \t --> \\t */
*to++= '\\';
*to++= 't';
break;
case '\\': /* \ --> \\ */
*to++= '\\';
*to++= '\\';
break;
case '\'': /* ' --> \' */
*to++= '\\';
*to++= '\'';
break;
case '"': /* " --> \" */
*to++= '\\'; /* Better safe than sorry */
*to++= '"';
break;
case '\032': /* Ctrl-Z (Win32 EOF) --> \\Z */
if (data->dbtype_id == DB_MYSQL)
{
*to++= '\\'; /* This gives problems on Win32 */
*to++= 'Z';
}
else
{
*to++= *from;
}
break;
default: /* copy character directly */
*to++= *from;
}
}
}
else
#endif
{
for (end=from+from_length; from != end; from++)
{
switch(*from)
{
case '\'': /* ' --> '' */
*to++= '\'';
*to++= '\'';
break;
default: /* copy character directly */
*to++= *from;
}
}
}
*to=0;
return(char *)to_start;
}
/*
Same function as above but will work on a static buffer, slightly different arguments...
*/
u_int32_t snort_escape_string_STATIC(char *from, u_int32_t buffer_max_len ,DatabaseData *data)
{
#if defined(ENABLE_POSTGRESQL)
int error = 0;
size_t write_len = 0;
#endif /* defined(ENABLE_POSRGRESQL) */
char * to = NULL;
char * to_start = NULL;
char * end = NULL;
char * from_start = NULL;
int from_length = 0;
if( (from == NULL) ||
(data == NULL))
{
/* XXX */
return 1;
}
if( (buffer_max_len > (DATABASE_MAX_ESCAPE_STATIC_BUFFER_LEN - 1)) ||
( (strlen(from) + 1 ) > buffer_max_len) ||
(buffer_max_len == 0))
{
/* XXX */
FatalError("database [%s()]: Edit source code and change the value of the #define DATABASE_MAX_ESCAPE_STATIC_BUFFER_LEN in spo_database.h to something greater than [%u] \n",
__FUNCTION__,
buffer_max_len);
}
memset(data->sanitize_buffer,'\0',DATABASE_MAX_ESCAPE_STATIC_BUFFER_LEN);
if( (from_length = strlen(from)) == 1)
{
/* Nothing to escape */
return 0;
}
from_start = from;
to = data->sanitize_buffer;
to_start = to;
switch(data->dbtype_id)
{
#ifdef ENABLE_ORACLE
case DB_ORACLE:
for (end=from+from_length; from != end; from++)
{
switch(*from)
{
case '\'': /* ' --> '' */
*to++= '\'';
*to++= '\'';
break;
case '\032': /* Ctrl-Z (Win32 EOF) --> \\Z */
*to++= '\\'; /* This gives problems on Win32 */
*to++= 'Z';
break;
default: /* copy character directly */
*to++= *from;
}
}
break;
#endif
#ifdef ENABLE_MSSQL
case DB_MSSQL:
for (end=from+from_length; from != end; from++)
{
switch(*from)
{
case '\'': /* ' --> '' */
*to++= '\'';
*to++= '\'';
break;
default: /* copy character directly */
*to++= *from;
}
}
break;
#endif
/* Historically these were together in a common "else".
* Keeping it that way until somebody complains...
*/
#if defined( ENABLE_MYSQL ) || defined (ENABLE_ODBC)
//#ifdef ENABLE_MYSQL
case DB_ODBC:
case DB_MYSQL:
for(end=from+from_length; from != end; from++)
{
switch(*from)
{
/*
* Only need to escape '%' and '_' characters
* when querying a SELECT...LIKE, which never
* occurs in Snort. Excluding these checks
* for that reason.
*/
/*
case '%': * % --> \% *
*to++= '\\';
*to++= '%';
break;
case '_': * _ --> \_ *
*to++= '\\';
*to++= '_';
break;
*/
case 0: /* NULL --> \\0 (probably never encountered due to strlen() above) */
*to++= '\\'; /* Must be escaped for 'mysql' */
*to++= '0';
break;
case '\n': /* \n --> \\n */
*to++= '\\'; /* Must be escaped for logs */
*to++= 'n';
break;
case '\r': /* \r --> \\r */
*to++= '\\';
*to++= 'r';
break;
case '\t': /* \t --> \\t */
*to++= '\\';
*to++= 't';
break;
case '\\': /* \ --> \\ */
*to++= '\\';
*to++= '\\';
break;
case '/':
*to++= '\\'; /* / --> \/ */
*to++= '/';
break;
case '\'': /* ' --> \' */
*to++= '\\';
*to++= '\'';
break;
case '"': /* " --> \" */
*to++= '\\'; /* Better safe than sorry */
*to++= '"';
break;
case '\032': /* Ctrl-Z (Win32 EOF) --> \\Z */
if (data->dbtype_id == DB_MYSQL)
{
*to++= '\\'; /* This gives problems on Win32 */
*to++= 'Z';
}
else
{
*to++= *from;
}
break;
default: /* copy character directly */
*to++= *from;
}
}
break;
#endif /* defined( ENABLE_MYSQL ) || defined (ENABLE_ODBC) */
#ifdef ENABLE_POSTGRESQL
case DB_POSTGRESQL:
if( (write_len = PQescapeStringConn(data->p_connection,
data->sanitize_buffer,
from,
buffer_max_len,&error)) == 0)
{
/* XXX */
return 1;
}
if(error != 1)
{
memcpy(from_start,data->sanitize_buffer,write_len+1);
}
else
{
/* XXX */
return 1;
}
return 0;
break;
#endif /* ENABLE_POSTGRESQL*/
default:
for (end=from+from_length; from != end; from++)
{
switch(*from)
{
case '\'': /* ' --> '' */
*to++= '\'';
*to++= '\'';
break;
case '\\': /* \ --> \\ */
*to++= '\\';
*to++= '\\';
break;
default: /* copy character directly */
*to++= *from;
}
}
break;
}
*to='\0';
if(strlen(to_start) > buffer_max_len)
{
/* XXX */
return 1;
}
memcpy(from_start,to_start,strlen(to_start));
return 0;
}
/*******************************************************************************
* Function: UpdateLastCid(DatabaseData * data, int sid, int cid)
*
* Purpose: Sets the last cid used for a given a sensor ID (sid),
*
* Arguments: data : database information
* sid : sensor ID
* cid : event ID
*
* Returns: status of the update
*
******************************************************************************/
int UpdateLastCid(DatabaseData *data, int sid, int cid)
{
DatabaseCleanInsert(data);
if( BeginTransaction(data) )
{
/* XXX */
FatalError("database [%s()]: Failed to Initialize transaction, bailing ... \n",
__FUNCTION__);
}
if( (SnortSnprintf(data->SQL_INSERT, MAX_QUERY_LENGTH,
"UPDATE sensor "
"SET last_cid = %u "
"WHERE sid = %u;",
cid, sid)) != SNORT_SNPRINTF_SUCCESS)
{
/* XXX */
return 1;
}
if(Insert(data->SQL_INSERT, data,0))
{
/* XXX */
return 1;
}
if(CommitTransaction(data))
{
/* XXX */
ErrorMessage("ERROR database: [%s()]: Error commiting transaction \n",
__FUNCTION__);
setTransactionCallFail(&data->dbRH[data->dbtype_id]);
return 1;
}
else
{
resetTransactionState(&data->dbRH[data->dbtype_id]);
}
return 0;
}
/*******************************************************************************
* Function: GetLastCid(DatabaseData * data, int sid)
*
* Purpose: Returns the last cid used for a given a sensor ID (sid),
*
* Arguments: data : database information
* sid : sensor ID
*
* Returns: last cid for a given sensor ID (sid)
*
******************************************************************************/
int GetLastCid(DatabaseData *data, int sid,u_int32_t *r_cid)
{
if(r_cid == NULL)
{
/* XXX */
return 1;
}
DatabaseCleanSelect(data);
if( (SnortSnprintf(data->SQL_SELECT, MAX_QUERY_LENGTH,
"SELECT last_cid "
" FROM sensor "
" WHERE sid = %u", sid)) != SNORT_SNPRINTF_SUCCESS)
{
*r_cid = 0;
return 1;
}
if( Select(data->SQL_SELECT,data,(u_int32_t *)r_cid))
{
/* XXX */
ErrorMessage("ERROR database: executing Select() with Query [%s] \n",data->SQL_SELECT);
*r_cid = 0;
return 1;
}
return 0;
}
/*******************************************************************************
* Function: CheckDBVersion(DatabaseData * data)
*
* Purpose: To determine the version number of the underlying DB schema
*
* Arguments: database information
*
* Returns: version number of the schema
*
******************************************************************************/
int CheckDBVersion(DatabaseData * data)
{
if(data == NULL)
{
/* XXX */
return 1;
}
DatabaseCleanSelect(data);
#if defined(ENABLE_MSSQL) || defined(ENABLE_ODBC)
// if ( data->dbtype_id == DB_MSSQL ||
// (data->dbtype_id==DB_ODBC && data->u_underlying_dbtype_id==DB_MSSQL) )
if(data->dbtype_id == DB_ODBC)
{
/* "schema" is a keyword in SQL Server, so use square brackets
* to indicate that we are referring to the table
*/
if( (SnortSnprintf(data->SQL_SELECT, MAX_QUERY_LENGTH,
"SELECT vseq FROM [schema]")) != SNORT_SNPRINTF_SUCCESS)
{
return -1;
}
}
else
#endif
{
#if defined(ENABLE_MYSQL)
if (data->dbtype_id == DB_MYSQL)
{
/* "schema" is a keyword in MYSQL, so use `schema`
* to indicate that we are referring to the table
*/
if( (SnortSnprintf(data->SQL_SELECT, MAX_QUERY_LENGTH,
"SELECT vseq FROM `schema`")) != SNORT_SNPRINTF_SUCCESS)
{
return -1;
}
}
else
#endif
{
if( (SnortSnprintf(data->SQL_SELECT, MAX_QUERY_LENGTH,
"SELECT vseq FROM schema")) != SNORT_SNPRINTF_SUCCESS)
{
return -1;
}
}
}
if( Select(data->SQL_SELECT,data,(u_int32_t *)&data->DBschema_version))
{
/* XXX */
ErrorMessage("ERROR database: executing Select() with Query [%s] \n",data->SQL_SELECT);
return 1;
}
if (data->DBschema_version == -1)
FatalError("database Unable to construct query - output error or truncation\n");
if ( data->DBschema_version == 0 )
{
FatalError(FATAL_BAD_SCHEMA_1, LATEST_DB_SCHEMA_VERSION, FATAL_BAD_SCHEMA_2);
}
if ( data->DBschema_version < LATEST_DB_SCHEMA_VERSION )
{
FatalError(FATAL_OLD_SCHEMA_1, data->DBschema_version, LATEST_DB_SCHEMA_VERSION, FATAL_OLD_SCHEMA_2);
}
return 0;
}
/*******************************************************************************
* Function: BeginTransaction(DatabaseData * data)
*
* Purpose: Database independent SQL to start a transaction
*
******************************************************************************/
u_int32_t BeginTransaction(DatabaseData * data)
{
if(data == NULL)
{
/* XXX */
FatalError("database [%s()], Invoked with NULL DatabaseData \n",
__FUNCTION__);
}
if(checkTransactionState(&data->dbRH[data->dbtype_id]))
{
/* We already are in a transaction, possible nested call do not sub BEGIN..*/
return 0;
}
switch(data->dbtype_id)
{
#ifdef ENABLE_ODBC
case DB_ODBC:
setTransactionState(&data->dbRH[data->dbtype_id]);
return 0;
break;
#endif
#ifdef ENABLE_MSSQL
case DB_MSSQL:
setTransactionState(&data->dbRH[data->dbtype_id]);
if( Insert("BEGIN TRANSACTION", data,0))
{
/*XXX */
return 1;
}
return 0;
break;
#endif
#ifdef ENABLE_ORACLE
case DB_ORACLE:
/* Do nothing. Oracle will implicitly create a transaction. */
/* CHECK -elz i will have to check on that */
return 0;
break;
#endif
default:
setTransactionState(&data->dbRH[data->dbtype_id]);
if( Insert("BEGIN;", data,0))
{
/*XXX */
return 1;
}
return 0;
break;
}
/* XXX */
return 1;
}
/*******************************************************************************
* Function: CommitTransaction(DatabaseData * data)
*
* Purpose: Database independent SQL to commit a transaction
*
******************************************************************************/
u_int32_t CommitTransaction(DatabaseData * data)
{
if(data == NULL)
{
/* XXX */
FatalError("database [%s()], Invoked with NULL DatabaseData \n",
__FUNCTION__);
}
if((checkTransactionState(&data->dbRH[data->dbtype_id])) == 0)
{
/* We are not in a transaction, effect of some possible nested call
be quiet */
return 0;
}
switch(data->dbtype_id)
{
#ifdef ENABLE_ODBC
case DB_ODBC:
//if( SQLEndTran(SQL_HANDLE_DBC, data->u_connection, SQL_COMMIT) != SQL_SUCCESS )
//{
//ODBCPrintError(data,SQL_HANDLE_DBC);
//}
goto transaction_success;
break;
#endif
#ifdef ENABLE_MSSQL
case DB_MSSQL:
if( Insert("COMMIT TRANSACTION", data,1))
{
/* XXX */
return 1;
}
goto transaction_success;
break;
#endif
#ifdef ENABLE_ORACLE
case DB_ORACLE:
return Insert("COMMIT WORK", data,1);
break;
#endif
default:
if( Insert("COMMIT;", data,1))
{
/*XXX */
return 1;
}
goto transaction_success;
break;
}
/* XXX */
return 1;
transaction_success:
/* Reset the transaction error count */
resetTransactionState(&data->dbRH[data->dbtype_id]);
return 0;
}
/*******************************************************************************
* Function: RollbackTransaction(DatabaseData * data)
*
* Purpose: Database independent SQL to rollback a transaction
*
******************************************************************************/
u_int32_t RollbackTransaction(DatabaseData * data)
{
if(data == NULL)
{
/* XXX */
FatalError("database [%s()], Invoked with NULL DatabaseData \n",
__FUNCTION__);
}
if(data->dbRH[data->dbtype_id].transactionErrorCount > data->dbRH[data->dbtype_id].transactionErrorThreshold)
{
/* XXX */
LogMessage("[%s(): Call failed, we reached the maximum number of transaction error [%u] \n",
__FUNCTION__,
data->dbRH[data->dbtype_id].transactionErrorThreshold);
return 1;
}
if( (data->dbRH[data->dbtype_id].dbConnectionStatus(&data->dbRH[data->dbtype_id])))
{
/* XXX */
LogMessage("[%s()] Call failed check to dbConnectionStatus()\n",
__FUNCTION__);
return 1;
}
if((checkTransactionState(&data->dbRH[data->dbtype_id])) == 0)
{
/* We reached a rollback when not in transaction state announce it */
LogMessage("[%s()] : called while not in transaction \n",
__FUNCTION__);
return 1;
}
if(getReconnectState(&data->dbRH[data->dbtype_id]))
{
/* Since We could get called from different places we are gown up and reset out self. */
resetTransactionState(&data->dbRH[data->dbtype_id]);
/* We reconnected, transaction call failed , we can't call "ROLLBACK" since the transaction should have aborted */
/* We reset state */
setReconnectState(&data->dbRH[data->dbtype_id],0);
return 0;
}
switch(data->dbtype_id)
{
#ifdef ENABLE_ODBC
case DB_ODBC:
// if( SQLEndTran(SQL_HANDLE_DBC, data->u_connection, SQL_ROLLBACK) != SQL_SUCCESS )
// {
// ODBCPrintError(data,SQL_HANDLE_DBC);
// return 1;
// }
return 0;
break;
#endif
#ifdef ENABLE_MSSQL
case DB_MSSQL:
return Insert("ROLLBACK TRANSACTION;", data,0);
break;
#endif
#ifdef ENABLE_ORACLE
case DB_ORACLE:
return Insert("ROLLBACK WORK;", data,0);
break;
#endif
default:
return Insert("ROLLBACK;", data,0);
}
/* XXX */
return 1;
}
/*******************************************************************************
* Function: Insert(char * query, DatabaseData * data)
*
* Purpose: Database independent function for SQL inserts
*
* Arguments: query (An SQL insert)
*
* Returns:
* 0 OK
* 1 Error
******************************************************************************/
int Insert(char * query, DatabaseData * data,u_int32_t inTransac)
{
#ifdef ENABLE_ODBC
long fRes = 0;
#endif
#if defined(ENABLE_MYSQL) || defined(ENABLE_POSTGRESQL)
int result = 0;
#endif /* defined(ENABLE_MYSQL) || defined(ENABLE_POSTGRESQL) */
if( (query == NULL) ||
(data == NULL) ||
checkDatabaseType(data))
{
/* XXX */
return 1;
}
/* This mainly has been set for Rollback */
if(inTransac == 1)
{
if(checkTransactionCall(&data->dbRH[data->dbtype_id]))
{
/* A This shouldn't happen since we are in failed transaction state */
/* XXX */
return 1;
}
}
if( (data->dbRH[data->dbtype_id].dbConnectionStatus(&data->dbRH[data->dbtype_id])))
{
/* XXX */
LogMessage("Insert Query[%s] failed check to dbConnectionStatus()\n",query);
return 1;
}
#ifdef ENABLE_POSTGRESQL
if( data->dbtype_id == DB_POSTGRESQL )
{
data->p_result = PQexec(data->p_connection,query);
if(!(PQresultStatus(data->p_result) != PGRES_COMMAND_OK))
{
result = 0;
}
else
{
if(PQerrorMessage(data->p_connection)[0] != '\0')
{
ErrorMessage("ERROR database: database: postgresql_error: %s\n",
PQerrorMessage(data->p_connection));
return 1;
}
}
PQclear(data->p_result);
data->p_result = NULL;
return 0;
}
#endif
#ifdef ENABLE_MYSQL
if(data->dbtype_id == DB_MYSQL)
{
result = mysql_query(data->m_sock,query);
switch (result)
{
case 0:
return 0;
break;
case CR_COMMANDS_OUT_OF_SYNC:
case CR_SERVER_GONE_ERROR:
case CR_UNKNOWN_ERROR:
default:
/* XXX */
/* Could lead to some corruption lets exit nicely .. */
/* Since this model of the database incluse alot of atomic queries .....*/
if( (mysql_errno(data->m_sock)))
{
FatalError("database mysql_error: %s\n\tSQL=[%s]\n",
mysql_error(data->m_sock),query);
}
else
{
/* XXX */
return 1;
}
break;
}
}
#endif
#ifdef ENABLE_ODBC
if(data->dbtype_id == DB_ODBC)
{
if(SQLAllocHandle(SQL_HANDLE_STMT,data->u_connection, &data->u_statement) == SQL_SUCCESS)
{
fRes = SQLExecDirect(data->u_statement,(ODBC_SQLCHAR *)query, SQL_NTS);
if( (fRes != SQL_SUCCESS) ||
(fRes != SQL_SUCCESS_WITH_INFO))
{
result = 0;
SQLFreeHandle(SQL_HANDLE_STMT,data->u_statement);
return 0;
}
else
{
LogMessage("execdirect failed \n");
}
}
else
{
LogMessage("stmtalloc failed \n");
}
LogMessage("[%s()], failed insert [%s], \n",
__FUNCTION__,
query);
ODBCPrintError(data,SQL_HANDLE_STMT);
SQLFreeHandle(SQL_HANDLE_STMT,data->u_statement);
}
#endif
#ifdef ENABLE_ORACLE
if(data->dbtype_id == DB_ORACLE)
{
char *blob = NULL;
/* If BLOB type - split query to actual SQL and blob to BLOB data */
if(strncasecmp(query,"INSERT INTO data",16)==0 || strncasecmp(query,"INSERT INTO opt",15)==0)
{
if((blob=strchr(query,'|')) != NULL)
{
*blob='\0'; blob++;
}
}
if(OCI_SUCCESS == OCIStmtPrepare(data->o_statement
, data->o_error
, query
, strlen(query)
, OCI_NTV_SYNTAX
, OCI_DEFAULT))
{
if( blob != NULL )
{
OCIBindByPos(data->o_statement
, &data->o_bind
, data->o_error
, 1
, (dvoid *)blob
, strlen(blob)
, SQLT_BIN
, 0
, 0
, 0
, 0
, 0
, OCI_DEFAULT);
}
if(OCI_SUCCESS == OCIStmtExecute(data->o_servicecontext
, data->o_statement
, data->o_error
, 1
, 0
, NULL
, NULL
, OCI_COMMIT_ON_SUCCESS))
{
result = 0;
}
}
if( result != 1 )
{
OCIErrorGet(data->o_error
, 1
, NULL
, &data->o_errorcode
, data->o_errormsg
, sizeof(data->o_errormsg)
, OCI_HTYPE_ERROR);
ErrorMessage("ERROR database: database: oracle_error: %s\n", data->o_errormsg);
ErrorMessage(" : query: %s\n", query);
}
}
#endif
#ifdef ENABLE_MSSQL
if(data->dbtype_id == DB_MSSQL)
{
SAVESTATEMENT(query);
dbfreebuf(data->ms_dbproc);
if( dbcmd(data->ms_dbproc, query) == SUCCEED )
if( dbsqlexec(data->ms_dbproc) == SUCCEED )
if( dbresults(data->ms_dbproc) == SUCCEED )
{
while (dbnextrow(data->ms_dbproc) != NO_MORE_ROWS)
{
result = (int)data->ms_col;
}
result = 0;
}
CLEARSTATEMENT();
}
#endif
return 1;
}
/*******************************************************************************
* Function: Select(char * query, DatabaeData * data, u_int32_t *rval)
*
*
*
* Returns:
* 0 OK
* 1 ERROR
******************************************************************************/
int Select(char * query, DatabaseData * data,u_int32_t *rval)
{
#if defined(ENABLE_MYSQL) || defined(ENABLE_POSTGRESQL)
int result = 0;
#endif /* defined(ENABLE_MYSQL) || defined(ENABLE_POSTGRESQL) */
if( (query == NULL) ||
(data == NULL) ||
(rval == NULL))
{
/* XXX */
FatalError("database [%s()] Invoked with a NULL argument Query [0x%x] Data [0x%x] rval [0x%x] \n",
__FUNCTION__,
query,
data,
rval);
}
if(checkTransactionCall(&data->dbRH[data->dbtype_id]))
{
/* A This shouldn't happen since we are in failed transaction state */
/* XXX */
return 1;
}
#if defined(ENABLE_MYSQL)
Select_reconnect:
#endif /* defined(ENABLE_MYSQL) */
if( (data->dbRH[data->dbtype_id].dbConnectionStatus(&data->dbRH[data->dbtype_id])))
{
/* XXX */
FatalError("database Select Query[%s] failed check to dbConnectionStatus()\n",query);
}
switch(data->dbtype_id)
{
#ifdef ENABLE_POSTGRESQL
case DB_POSTGRESQL:
data->p_result = PQexec(data->p_connection,query);
if((PQresultStatus(data->p_result) == PGRES_TUPLES_OK))
{
if(PQntuples(data->p_result))
{
if((PQntuples(data->p_result)) > 1)
{
ErrorMessage("ERROR database: Query [%s] returned more than one result\n",
query);
result = 0;
return 1;
}
else
{
*rval = atoi(PQgetvalue(data->p_result,0,0));
}
}
}
if(!result)
{
if(PQerrorMessage(data->p_connection)[0] != '\0')
{
ErrorMessage("ERROR database: postgresql_error: %s\n",
PQerrorMessage(data->p_connection));
return 1;
}
}
PQclear(data->p_result);
data->p_result = NULL;
break;
#endif
#ifdef ENABLE_MYSQL
case DB_MYSQL:
result = mysql_query(data->m_sock,query);
switch(result)
{
case 0:
if( (data->m_result = mysql_use_result(data->m_sock)) == NULL)
{
/* XXX */
*rval = 0;
return 1;
}
else
{
if( (data->m_row = mysql_fetch_row(data->m_result)) == NULL)
{
/* XXX */
*rval = 0;
mysql_free_result(data->m_result);
data->m_result = NULL;
return 1;
}
else
{
if(data->m_row[0] != NULL)
{
*rval = atoi(data->m_row[0]);
}
else
{
/* XXX */
*rval = 0;
mysql_free_result(data->m_result);
data->m_result = NULL;
return 1;
}
}
mysql_free_result(data->m_result);
data->m_result = NULL;
return 0;
}
break;
case CR_COMMANDS_OUT_OF_SYNC:
case CR_SERVER_GONE_ERROR:
case CR_UNKNOWN_ERROR:
default:
if(checkTransactionState(data->dbRH))
{
LogMessage("[%s()]: Failed executing with error [%s], in transaction will Abort. \n"
"\t Failed QUERY: [%s] \n",
__FUNCTION__,
mysql_error(data->m_sock),
query);
return 1;
}
LogMessage("[%s()]: Failed to execute query [%s] , will retry \n",
__FUNCTION__,
query);
goto Select_reconnect;
break;
}
/* XXX */
*rval = 0;
return 1;
break;
#endif
#ifdef ENABLE_ODBC
case DB_ODBC:
if(SQLAllocHandle(SQL_HANDLE_STMT,data->u_connection, &data->u_statement) == SQL_SUCCESS)
{
//if(SQLPrepare(data->u_statement, (ODBC_SQLCHAR *)query, SQL_NTS) == SQL_SUCCESS)
//{
//if(SQLExecute(data->u_statement) == SQL_SUCCESS)
if(SQLExecDirect(data->u_statement,(ODBC_SQLCHAR *)query, SQL_NTS) == SQL_SUCCESS)
{
if(SQLRowCount(data->u_statement, &data->u_rows) == SQL_SUCCESS)
{
if(data->u_rows)
{
if(data->u_rows > 1)
{
SQLFreeHandle(SQL_HANDLE_STMT,data->u_statement);
ErrorMessage("ERROR database: Query [%s] returned more than one result\n", query);
result = 0;
return 1;
}
else
{
if(SQLFetch(data->u_statement) == SQL_SUCCESS)
{
if(SQLGetData(data->u_statement,1,SQL_INTEGER,
&data->u_col,
sizeof(data->u_col), NULL) == SQL_SUCCESS)
{
*rval = (int)data->u_col;
SQLFreeHandle(SQL_HANDLE_STMT,data->u_statement);
}
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT,data->u_statement);
return 1;
}
}
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT,data->u_statement);
return 1;
}
}
}
}
break;
#endif
#ifdef ENABLE_ORACLE
case DB_ORACLE:
int success = 0; /* assume it will fail */
if(OCI_SUCCESS == OCIStmtPrepare(data->o_statement
, data->o_error
, query
, strlen(query)
, OCI_NTV_SYNTAX
, OCI_DEFAULT))
{
if(OCI_SUCCESS == OCIDefineByPos(data->o_statement
, &data->o_define
, data->o_error
, 1
, &result
, sizeof(result)
, SQLT_INT
, 0
, 0
, 0
, OCI_DEFAULT))
{
sword status;
status = OCIStmtExecute(data->o_servicecontext
, data->o_statement
, data->o_error
, 1 /*0*/
, 0
, NULL
, NULL
, OCI_DEFAULT);
if( status==OCI_SUCCESS || status==OCI_NO_DATA )
{
success = 1;
}
}
}
if( ! success )
{
OCIErrorGet(data->o_error
, 1
, NULL
, &data->o_errorcode
, data->o_errormsg
, sizeof(data->o_errormsg)
, OCI_HTYPE_ERROR);
ErrorMessage("ERROR database: database: oracle_error: %s\n", data->o_errormsg);
ErrorMessage(" : query: %s\n", query);
}
break;
#endif
#ifdef ENABLE_MSSQL
case DB_MSSQL:
SAVESTATEMENT(query);
dbfreebuf(data->ms_dbproc);
if( dbcmd(data->ms_dbproc, query) == SUCCEED )
if( dbsqlexec(data->ms_dbproc) == SUCCEED )
if( dbresults(data->ms_dbproc) == SUCCEED )
if( dbbind(data->ms_dbproc, 1, INTBIND, (DBINT) 0, (BYTE *) &data->ms_col) == SUCCEED )
while (dbnextrow(data->ms_dbproc) != NO_MORE_ROWS)
{
result = (int)data->ms_col;
}
CLEARSTATEMENT();
break;
#endif
default:
FatalError("database [%s()]: Invoked with unknown database type [%u] \n",
__FUNCTION__,
data->dbtype_id);
}
return 0;
}
/*******************************************************************************
* Function: Connect(DatabaseData * data)
*
* Purpose: Database independent function to initiate a database
* connection
*
******************************************************************************/
void Connect(DatabaseData * data)
{
#ifdef ENABLE_ODBC
ODBC_SQLRETURN ret;
#endif /* ENABLE_ODBC */
if(data == NULL)
{
/* XXX */
FatalError("database [%s()]: Invoked with NULL DatabaseData argument \n",
__FUNCTION__);
}
switch(data->dbtype_id)
{
#ifdef ENABLE_POSTGRESQL
case DB_POSTGRESQL:
if (data->use_ssl == 1)
{
data->p_connection =
PQsetdbLogin(data->host,
data->port,
data->dbRH[data->dbtype_id].ssl_mode,
NULL,
data->dbname,
data->user,
data->password);
}
else
{
data->p_connection =
PQsetdbLogin(data->host,
data->port,
NULL,
NULL,
data->dbname,
data->user,
data->password);
}
if(PQstatus(data->p_connection) == CONNECTION_BAD)
{
PQfinish(data->p_connection);
FatalError("database Connection to database '%s' failed\n", data->dbname);
}
break;
#endif
#ifdef ENABLE_MYSQL
case DB_MYSQL:
data->m_sock = mysql_init(NULL);
if(data->m_sock == NULL)
{
FatalError("database Connection to database '%s' failed\n", data->dbname);
}
/* check if we want to connect with ssl options */
if (data->use_ssl == 1)
{
mysql_ssl_set(data->m_sock,
data->dbRH[data->dbtype_id].ssl_key,
data->dbRH[data->dbtype_id].ssl_cert,
data->dbRH[data->dbtype_id].ssl_ca,
data->dbRH[data->dbtype_id].ssl_ca_path,
data->dbRH[data->dbtype_id].ssl_cipher);
}
if(mysql_real_connect(data->m_sock,
data->host,
data->user,
data->password,
data->dbname,
data->port == NULL ? 0 : atoi(data->port), NULL, 0) == NULL)
{
if(mysql_errno(data->m_sock))
FatalError("database mysql_error: %s\n", mysql_error(data->m_sock));
FatalError("database Failed to logon to database '%s'\n", data->dbname);
}
if(mysql_autocommit(data->m_sock,0))
{
/* XXX */
LogMessage("WARNING database: unable to unset autocommit\n");
return ;
}
data->dbRH[data->dbtype_id].pThreadID = mysql_thread_id(data->m_sock);
break;
#endif /* ENABLE_MYSQL */
#ifdef ENABLE_ODBC
case DB_ODBC:
data->u_underlying_dbtype_id = DB_UNDEFINED;
if(!(SQLAllocEnv(&data->u_handle) == SQL_SUCCESS))
{
FatalError("database unable to allocate ODBC environment\n");
}
if(!(SQLAllocConnect(data->u_handle, &data->u_connection) == SQL_SUCCESS))
{
FatalError("database unable to allocate ODBC connection handle\n");
}
/* The SQL Server ODBC driver always returns SQL_SUCCESS_WITH_INFO
* on a successful SQLConnect, SQLDriverConnect, or SQLBrowseConnect.
* When an ODBC application calls SQLGetDiagRec after getting
* SQL_SUCCESS_WITH_INFO, it can receive the following messages:
* 5701 - Indicates that SQL Server put the user's context into the
* default database defined in the data source, or into the
* default database defined for the login ID used in the
* connection if the data source did not have a default database.
* 5703 - Indicates the language being used on the server.
* You can ignore messages 5701 and 5703; they are only informational.
*/
ret = SQLConnect( data->u_connection
, (ODBC_SQLCHAR *)data->dbname
, SQL_NTS
, (ODBC_SQLCHAR *)data->user
, SQL_NTS
, (ODBC_SQLCHAR *)data->password
, SQL_NTS);
if( (ret != SQL_SUCCESS) &&
(ret != SQL_SUCCESS_WITH_INFO))
{
ODBCPrintError(data,SQL_HANDLE_DBC);
FatalError("database ODBC unable to connect.\n");
}
/* NOTE: -elz
The code below was commented for review, since we want to streamline the api and remove
all SQLGetDiagRec call's.
*/
//int encounteredFailure = 1; /* assume there is an error */
/*
char odbcError[2000];
odbcError[0] = '\0';
if( ret == SQL_SUCCESS_WITH_INFO )
{
ODBC_SQLCHAR sqlState[6];
ODBC_SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER nativeError;
SQLSMALLINT errorIndex = 1;
SQLSMALLINT msgLen;
*/
/* assume no error unless nativeError tells us otherwise */
//encounteredFailure = 0;
/*
while ((ret = SQLGetDiagRec( SQL_HANDLE_DBC
, data->u_connection
, errorIndex
, sqlState
, &nativeError
, msg
, SQL_MAX_MESSAGE_LENGTH
, &msgLen)) != SQL_NO_DATA)
{
if( strstr((const char *)msg, "SQL Server") != NULL )
{
data->u_underlying_dbtype_id = DB_MSSQL;
}
if( nativeError!=5701 && nativeError!=5703 )
{
encounteredFailure = 1;
strncat(odbcError, (const char *)msg, sizeof(odbcError));
}
errorIndex++;
}
}
if( encounteredFailure )
{
}
*/
break;
#endif
#ifdef ENABLE_ORACLE
case DB_ORACLE:
#define PRINT_ORACLE_ERR(func_name) \
{ \
OCIErrorGet(data->o_error, 1, NULL, &data->o_errorcode, \
data->o_errormsg, sizeof(data->o_errormsg), OCI_HTYPE_ERROR); \
ErrorMessage("ERROR database: Oracle_error: %s\n", data->o_errormsg); \
FatalError("database %s : Connection to database '%s' failed\n", \
func_name, data->dbRH[data->dbtype_id]->dbname); \
}
if (!getenv("ORACLE_HOME"))
{
ErrorMessage("ERROR database: ORACLE_HOME environment variable not set\n");
}
if (!data->user || !data->password || !data->dbRH[data->dbtype_id]->dbname)
{
ErrorMessage("ERROR database: user, password and dbname required for Oracle\n");
ErrorMessage("ERROR database: dbname must also be in tnsnames.ora\n");
}
if (data->host)
{
ErrorMessage("ERROR database: hostname not required for Oracle, use dbname\n");
ErrorMessage("ERROR database: dbname must be in tnsnames.ora\n");
}
if (OCIInitialize(OCI_DEFAULT, NULL, NULL, NULL, NULL))
PRINT_ORACLE_ERR("OCIInitialize");
if (OCIEnvInit(&data->o_environment, OCI_DEFAULT, 0, NULL))
PRINT_ORACLE_ERR("OCIEnvInit");
if (OCIEnvInit(&data->o_environment, OCI_DEFAULT, 0, NULL))
PRINT_ORACLE_ERR("OCIEnvInit (2)");
if (OCIHandleAlloc(data->o_environment, (dvoid **)&data->o_error, OCI_HTYPE_ERROR, (size_t) 0, NULL))
PRINT_ORACLE_ERR("OCIHandleAlloc");
if (OCILogon(data->o_environment, data->o_error, &data->o_servicecontext,
data->user, strlen(data->user), data->password, strlen(data->password),
data->dbRH[data->dbtype_id]->dbname, strlen(data->dbRH[data->dbtype_id]->dbname)))
{
OCIErrorGet(data->o_error, 1, NULL, &data->o_errorcode, d