Main Page | Modules | Class List | Directories | File List | Class Members | File Members | Related Pages

spo_database.c

Go to the documentation of this file.
00001 /*
00002 ** Portions Copyright (C) 2000,2001,2002 Carnegie Mellon University
00003 ** Copyright (C) 2001 Jed Pickel <jed@pickel.net>
00004 ** Portions Copyright (C) 2001 Andrew R. Baker <andrewb@farm9.com>
00005 **
00006 ** This program is free software; you can redistribute it and/or modify
00007 ** it under the terms of the GNU General Public License as published by
00008 ** the Free Software Foundation; either version 2 of the License, or
00009 ** (at your option) any later version.
00010 **
00011 ** This program is distributed in the hope that it will be useful,
00012 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 ** GNU General Public License for more details.
00015 **
00016 ** You should have received a copy of the GNU General Public License
00017 ** along with this program; if not, write to the Free Software
00018 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 */
00020 
00021 /* $Id$ */
00022 
00023 /* Snort Database Output Plug-in
00024  * 
00025  *  Maintainer: Roman Danyliw <rdd@cert.org>, <roman@danyliw.com>
00026  *
00027  *  Originally written by Jed Pickel <jed@pickel.net> (2000-2001)
00028  *
00029  * See the doc/README.database file with this distribution 
00030  * documentation or the snortdb web site for configuration
00031  * information
00032  *
00033  * Web Site: http://www.andrew.cmu.edu/~rdanyliw/snortdb/snortdb.html
00034  */
00035 
00036 /******** Configuration *************************************************/
00037 
00038 /* 
00039  * If you want extra debugging information for solving database 
00040  * configuration problems, uncomment the following line. 
00041  */
00042 /* #define DEBUG */
00043 
00044 /* Enable DB transactions */
00045 #define ENABLE_DB_TRANSACTIONS
00046 
00047 /******** Headers ******************************************************/
00048 
00049 #ifdef HAVE_CONFIG_H
00050 #include "config.h"
00051 #endif
00052 
00053 #include <sys/types.h>
00054 #include <stdlib.h>
00055 #include <string.h>
00056 
00057 #include "event.h"
00058 #include "decode.h"
00059 #include "rules.h"
00060 #include "plugbase.h"
00061 #include "spo_plugbase.h"
00062 #include "parser.h"
00063 #include "debug.h"
00064 #include "util.h"
00065 
00066 #include "snort.h"
00067 #include "inline.h"
00068 
00069 #ifdef ENABLE_POSTGRESQL
00070     #include <libpq-fe.h>
00071 #endif
00072 #ifdef ENABLE_MYSQL
00073     #if defined(_WIN32) || defined(_WIN64)
00074         #include <windows.h>
00075     #endif
00076     #include <mysql.h>
00077 #endif
00078 #ifdef ENABLE_ODBC
00079     #include <sql.h>
00080     #include <sqlext.h>
00081     #include <sqltypes.h>
00082     /* The SQL Server libraries, for some reason I can't
00083      * understand, define their own constants for SQLRETURN
00084      * and SQLCHAR.  But, in SQL Server, these are numeric
00085      * values, not datatypes.  So we define datatypes here
00086      * with a non-conflicting name.
00087      */
00088     typedef SQLRETURN ODBC_SQLRETURN;
00089     typedef SQLCHAR   ODBC_SQLCHAR;
00090 #endif
00091 #ifdef ENABLE_ORACLE
00092     #include <oci.h>
00093 #endif
00094 #ifdef ENABLE_MSSQL
00095     #define DBNTWIN32
00096     #include <windows.h>
00097     #include <sqlfront.h>
00098     #include <sqldb.h>
00099 #endif
00100 
00101 /******** Data Types  **************************************************/
00102 
00103 /* enumerate the supported databases */
00104 enum db_types_en
00105 {
00106     DB_UNDEFINED  = 0,
00107     DB_MYSQL      = 1,
00108     DB_POSTGRESQL = 2,
00109     DB_MSSQL      = 3,
00110     DB_ORACLE     = 4,
00111     DB_ODBC       = 5
00112 };
00113 typedef enum db_types_en dbtype_t;
00114 
00115 /* link-list of SQL queries */
00116 typedef struct _SQLQuery
00117 {
00118     char * val;
00119     struct _SQLQuery * next;
00120 } SQLQuery;
00121 
00122 
00123 /* the cid is unique across the dbtype, dbname, host, and sid */
00124 /* therefore, we use these as a lookup key for the cid */
00125 typedef struct _SharedDatabaseData
00126 {
00127     dbtype_t  dbtype_id;
00128     char     *dbname;
00129     char     *host;
00130     int       sid;
00131     int       cid;
00132     int       reference;
00133 } SharedDatabaseData;
00134 
00135 typedef struct _DatabaseData
00136 {
00137     SharedDatabaseData *shared;
00138     char  *facility;
00139     char  *password;
00140     char  *user;
00141     char  *port;
00142     char  *sensor_name;
00143     int    encoding;
00144     int    detail;
00145     int    ignore_bpf;
00146     int    tz;
00147     int    DBschema_version;
00148 #ifdef ENABLE_POSTGRESQL
00149     PGconn * p_connection;
00150     PGresult * p_result;
00151 #endif
00152 #ifdef ENABLE_MYSQL
00153     MYSQL * m_sock;
00154     MYSQL_RES * m_result;
00155     MYSQL_ROW m_row;
00156 #endif
00157 #ifdef ENABLE_ODBC
00158     SQLHENV u_handle;
00159     SQLHDBC u_connection;
00160     SQLHSTMT u_statement;
00161     SQLINTEGER  u_col;
00162     SQLINTEGER  u_rows;
00163     dbtype_t    u_underlying_dbtype_id;
00164 #endif
00165 #ifdef ENABLE_ORACLE
00166     OCIEnv *o_environment;
00167     OCISvcCtx *o_servicecontext;
00168     OCIError *o_error;
00169     OCIStmt *o_statement;
00170     OCIDefine *o_define;
00171     text o_errormsg[512];
00172     sb4 o_errorcode;
00173 #endif
00174 #ifdef ENABLE_MSSQL
00175     PDBPROCESS  ms_dbproc;
00176     PLOGINREC   ms_login;
00177     DBINT       ms_col;
00178 #endif
00179 } DatabaseData;
00180 
00181 /* list for lookup of shared data information */
00182 typedef struct _SharedDatabaseDataNode
00183 {
00184     SharedDatabaseData *data;
00185     struct _SharedDatabaseDataNode *next;
00186 } SharedDatabaseDataNode;
00187 
00188 
00189 /******** Constants  ***************************************************/
00190 
00191 #define MAX_QUERY_LENGTH 8192
00192 #define KEYWORD_POSTGRESQL   "postgresql"
00193 #define KEYWORD_MYSQL        "mysql"
00194 #define KEYWORD_ODBC         "odbc"
00195 #define KEYWORD_ORACLE       "oracle"
00196 #define KEYWORD_MSSQL        "mssql"
00197 
00198 #define KEYWORD_HOST         "host"
00199 #define KEYWORD_PORT         "port"
00200 #define KEYWORD_USER         "user"
00201 #define KEYWORD_PASSWORD     "password"
00202 #define KEYWORD_DBNAME       "dbname"
00203 #define KEYWORD_SENSORNAME   "sensor_name"
00204 #define KEYWORD_ENCODING     "encoding"
00205     #define KEYWORD_ENCODING_HEX      "hex"
00206     #define KEYWORD_ENCODING_BASE64   "base64"
00207     #define KEYWORD_ENCODING_ASCII    "ascii"
00208 #define KEYWORD_DETAIL       "detail"
00209     #define KEYWORD_DETAIL_FULL  "full"
00210     #define KEYWORD_DETAIL_FAST  "fast"
00211 #define KEYWORD_IGNOREBPF    "ignore_bpf"
00212 #define KEYWORD_IGNOREBPF_NO   "no"
00213 #define KEYWORD_IGNOREBPF_ZERO "0"
00214 #define KEYWORD_IGNOREBPF_YES  "yes"
00215 #define KEYWORD_IGNOREBPF_ONE  "1"
00216 
00217 
00218 #define LATEST_DB_SCHEMA_VERSION 106
00219 
00220 /******** Prototypes  **************************************************/
00221 
00222 void          DatabaseInit(u_char *);
00223 DatabaseData *ParseDatabaseArgs(char *);
00224 void          Database(Packet *, char *, void *, Event *);
00225 char *        snort_escape_string(char *, DatabaseData *);
00226 void          SpoDatabaseCleanExitFunction(int, void *);
00227 void          SpoDatabaseRestartFunction(int, void *);
00228 void          InitDatabase();
00229 int           UpdateLastCid(DatabaseData *, int, int);
00230 int           GetLastCid(DatabaseData *, int);
00231 int           CheckDBVersion(DatabaseData *);
00232 void          BeginTransaction(DatabaseData * data);
00233 void          CommitTransaction(DatabaseData * data);
00234 void          RollbackTransaction(DatabaseData * data);
00235 int           Insert(char *, DatabaseData *);
00236 int           Select(char *, DatabaseData *);
00237 void          Connect(DatabaseData *);
00238 void          DatabasePrintUsage();
00239 void          FreeSharedDataList();
00240 
00241 /******** Global Variables  ********************************************/
00242 
00243 extern PV pv;
00244 extern OptTreeNode *otn_tmp;  /* rule node */
00245 
00246 static SharedDatabaseDataNode *sharedDataList = NULL;
00247 static int instances = 0;
00248 
00249 /******** Database Specific Extras  ************************************/
00250 
00251 /* The following is for supporting Microsoft SQL Server */
00252 #ifdef ENABLE_MSSQL
00253 
00254 /* If you want extra debugging information (specific to
00255    Microsoft SQL Server), uncomment the following line. */
00256 #define ENABLE_MSSQL_DEBUG
00257 
00258 #if defined(DEBUG) || defined(ENABLE_MSSQL_DEBUG)
00259     /* this is for debugging purposes only */
00260     static char g_CurrentStatement[2048];
00261     #define SAVESTATEMENT(str)   strncpy(g_CurrentStatement, str, sizeof(g_CurrentStatement) - 1);
00262     #define CLEARSTATEMENT()     bzero((char *) g_CurrentStatement, sizeof(g_CurrentStatement));
00263 #else
00264     #define SAVESTATEMENT(str)   NULL;
00265     #define CLEARSTATEMENT()     NULL;
00266 #endif /* DEBUG || ENABLE_MSSQL_DEBUG*/
00267 
00268     /* Prototype of SQL Server callback functions. 
00269      * See actual declaration elsewhere for details. 
00270      */
00271     static int mssql_err_handler(PDBPROCESS dbproc, int severity, int dberr, 
00272                                  int oserr, LPCSTR dberrstr, LPCSTR oserrstr);
00273     static int mssql_msg_handler(PDBPROCESS dbproc, DBINT msgno, int msgstate, 
00274                                  int severity, LPCSTR msgtext, LPCSTR srvname, LPCSTR procname, 
00275                                  DBUSMALLINT line);
00276 #endif /* ENABLE_MSSQL */
00277 
00278 /*******************************************************************************
00279  * Function: SetupDatabase()
00280  *
00281  * Purpose: Registers the output plugin keyword and initialization 
00282  *          function into the output plugin list.  This is the function that
00283  *          gets called from InitOutputPlugins() in plugbase.c.
00284  *
00285  * Arguments: None.
00286  *
00287  * Returns: void function
00288  *
00289  ******************************************************************************/
00290 void DatabaseSetup()
00291 {
00292     /* link the preprocessor keyword to the init function in 
00293        the preproc list */
00294     RegisterOutputPlugin("database", NT_OUTPUT_ALERT, DatabaseInit);
00295 
00296     DEBUG_WRAP(DebugMessage(DEBUG_INIT, "database(debug): database plugin is registered...\n"););
00297 }
00298 
00299 /*******************************************************************************
00300  * Function: DatabaseInit(u_char *)
00301  *
00302  * Purpose: Calls the argument parsing function, performs final setup on data
00303  *          structs, links the preproc function into the function list.
00304  *
00305  * Arguments: args => ptr to argument string
00306  *
00307  * Returns: void function
00308  *
00309  ******************************************************************************/
00310 void DatabaseInit(u_char *args)
00311 {
00312     DatabaseData *data = NULL;
00313     char * select_sensor_id = NULL;
00314     char * select_max_sensor_id = NULL;
00315     char * insert_into_sensor = NULL;
00316     int foundEntry = 0, sensor_cid, event_cid;
00317     SharedDatabaseDataNode *current = NULL;
00318     char * escapedSensorName = NULL;
00319     char * escapedInterfaceName = NULL;
00320 
00321 
00322     /* parse the argument list from the rules file */
00323     data = ParseDatabaseArgs(args);
00324 
00325     /* find a unique name for sensor if one was not supplied as an option */
00326     if(!data->sensor_name)
00327     {
00328         data->sensor_name = GetUniqueName((char *)PRINT_INTERFACE(pv.interface));
00329         if ( data->sensor_name )
00330         {
00331            if( data->sensor_name[strlen(data->sensor_name)-1] == '\n' )
00332            {
00333               data->sensor_name[strlen(data->sensor_name)-1] = '\0';
00334            }
00335 
00336            if( !pv.quiet_flag )
00337            {
00338                printf("database:   sensor name = %s\n", data->sensor_name);
00339            }
00340         }
00341     }
00342 
00343     data->tz = GetLocalTimezone();
00344 
00345     /* allocate memory for configuration queries */
00346     select_sensor_id     = (char *)SnortAlloc(MAX_QUERY_LENGTH);
00347     select_max_sensor_id = (char *)SnortAlloc(MAX_QUERY_LENGTH);
00348     insert_into_sensor   = (char *)SnortAlloc(MAX_QUERY_LENGTH);
00349 
00350     escapedSensorName    = snort_escape_string(data->sensor_name, data);
00351     if(pv.interface != NULL)
00352     {
00353         escapedInterfaceName = snort_escape_string(PRINT_INTERFACE(pv.interface), data);
00354     }
00355     else
00356     {   
00357        if(InlineMode())
00358        {
00359           escapedInterfaceName = snort_escape_string("inline", data);
00360        }
00361     }
00362     if( data->ignore_bpf == 0 )
00363     {
00364         if(pv.pcap_cmd == NULL)
00365         {
00366             snprintf(insert_into_sensor, MAX_QUERY_LENGTH, 
00367                      "INSERT INTO sensor (hostname, interface, detail, encoding, last_cid) "
00368                      "VALUES ('%s','%s','%u','%u', '0')", 
00369                      escapedSensorName,
00370                      escapedInterfaceName,
00371                      data->detail,
00372                      data->encoding);
00373             snprintf(select_sensor_id, MAX_QUERY_LENGTH, 
00374                      "SELECT sid "
00375                      "  FROM sensor "
00376                      " WHERE hostname = '%s' "
00377                      "   AND interface = '%s' "
00378                      "   AND detail = '%u' "
00379                      "   AND encoding = '%u' "
00380                      "   AND filter IS NULL",
00381                      escapedSensorName,
00382                      escapedInterfaceName,
00383                      data->detail,
00384                      data->encoding);
00385         }
00386         else
00387         {
00388             snprintf(insert_into_sensor, MAX_QUERY_LENGTH, 
00389                      "INSERT INTO sensor (hostname, interface, filter, detail, encoding, last_cid) "
00390                      "VALUES ('%s','%s','%s','%u','%u', '0')", 
00391                      escapedSensorName,
00392                      escapedInterfaceName,
00393                      pv.pcap_cmd,
00394                      data->detail,
00395                      data->encoding);
00396             snprintf(select_sensor_id, MAX_QUERY_LENGTH, 
00397                      "SELECT sid "
00398                      "  FROM sensor "
00399                      " WHERE hostname = '%s' "
00400                      "   AND interface = '%s' "
00401                      "   AND filter ='%s' "
00402                      "   AND detail = '%u' "
00403                      "   AND encoding = '%u'",
00404                      escapedSensorName,
00405                      escapedInterfaceName,
00406                      pv.pcap_cmd,
00407                      data->detail,
00408                      data->encoding);
00409         }
00410     }
00411     else /* ( data->ignore_bpf == 1 ) */
00412     {
00413         if(pv.pcap_cmd == NULL)
00414         {
00415             snprintf(insert_into_sensor, MAX_QUERY_LENGTH, 
00416                      "INSERT INTO sensor (hostname, interface, detail, encoding) "
00417                      "VALUES ('%s','%s','%u','%u')", 
00418                      escapedSensorName,
00419                      escapedInterfaceName,
00420                      data->detail,
00421                      data->encoding);
00422             snprintf(select_sensor_id, MAX_QUERY_LENGTH, 
00423                      "SELECT sid "
00424                      "  FROM sensor "
00425                      " WHERE hostname = '%s' "
00426                      "   AND interface = '%s' "
00427                      "   AND detail = '%u' "
00428                      "   AND encoding = '%u'",
00429                      escapedSensorName,
00430                      escapedInterfaceName,
00431                      data->detail,
00432                      data->encoding);
00433         }
00434         else
00435         {
00436             snprintf(insert_into_sensor, MAX_QUERY_LENGTH, 
00437                      "INSERT INTO sensor (hostname, interface, filter, detail, encoding) "
00438                      "VALUES ('%s','%s','%s','%u','%u')", 
00439                      escapedSensorName,
00440                      escapedInterfaceName,
00441                      pv.pcap_cmd,
00442                      data->detail, data->encoding);
00443             snprintf(select_sensor_id, MAX_QUERY_LENGTH, 
00444                      "SELECT sid "
00445                      "  FROM sensor "
00446                      " WHERE hostname = '%s' "
00447                      "   AND interface = '%s' "
00448                      "   AND detail = '%u' "
00449                      "   AND encoding = '%u'",
00450                      escapedSensorName,
00451                      escapedInterfaceName,
00452                      data->detail,
00453                      data->encoding);
00454         }
00455     }
00456 
00457     Connect(data);
00458 
00459     data->shared->sid = Select(select_sensor_id,data);
00460     if(data->shared->sid == 0)
00461     {
00462         Insert(insert_into_sensor,data);
00463         data->shared->sid = Select(select_sensor_id,data);
00464         if(data->shared->sid == 0)
00465         {
00466             ErrorMessage("database: Problem obtaining SENSOR ID (sid) from %s->sensor\n", 
00467                          data->shared->dbname);
00468             FatalError("\n"
00469                        " When this plugin starts, a SELECT query is run to find the sensor id for the\n"
00470                        " currently running sensor. If the sensor id is not found, the plugin will run\n"
00471                        " an INSERT query to insert the proper data and generate a new sensor id. Then a\n"
00472                        " SELECT query is run to get the newly allocated sensor id. If that fails then\n"
00473                        " this error message is generated.\n"
00474                        "\n"
00475                        " Some possible causes for this error are:\n"
00476                        "  * the user does not have proper INSERT or SELECT privileges\n"
00477                        "  * the sensor table does not exist\n"
00478                        "\n"
00479                        " If you are _absolutely_ certain that you have the proper privileges set and\n"
00480                        " that your database structure is built properly please let me know if you\n"
00481                        " continue to get this error. You can contact me at (roman@danyliw.com).\n"
00482                        "\n");
00483         }
00484     }
00485 
00486     if( !pv.quiet_flag )
00487     {
00488         printf("database:     sensor id = %u\n", data->shared->sid);
00489     }
00490 
00491     /* the cid may be shared across multiple instances of the database
00492      * plugin, first we check the shared data list to see if we already
00493      * have a value to use, if so, we replace the SharedDatabaseData struct
00494      * in the DatabaseData struct with the one out of the sharedDataList.
00495      * Sound confusing enough?  
00496      *   -Andrew    
00497      */
00498 
00499     /* XXX: Creating a set of list handling functions would make this cleaner */
00500     current = sharedDataList;
00501     while(current != NULL)
00502     {
00503         /* We have 4 key fields to check */
00504         if((current->data->sid == data->shared->sid) &&
00505            (current->data->dbtype_id == data->shared->dbtype_id) &&
00506            /* XXX: should this be a case insensitive compare? */
00507            (strcasecmp(current->data->dbname, data->shared->dbname) == 0) &&
00508            (strcasecmp(current->data->host, data->shared->host) == 0))
00509         {
00510             foundEntry = 1;
00511             break;
00512         }
00513         current = current->next;
00514     }
00515     
00516     if(foundEntry == 0)
00517     {
00518         /* Add it the the shared data list */
00519         SharedDatabaseDataNode *newNode = (SharedDatabaseDataNode *)SnortAlloc(sizeof(SharedDatabaseDataNode));
00520         newNode->data = data->shared;
00521         newNode->next = NULL;
00522         if(sharedDataList == NULL)
00523         {
00524             sharedDataList = newNode;
00525         }
00526         else
00527         {
00528             current = sharedDataList;
00529             while(current->next != NULL)
00530             {
00531                 current = current->next;
00532             }
00533             current->next = newNode;
00534         }
00535 
00536         /* Set the cid value 
00537          * - get the cid value in sensor.last_cid
00538          * - get the MAX(cid) from event 
00539          * - if snort crashed without storing the latest cid, then
00540          *     the MAX(event.cid) > sensor.last_cid.  Update last_cid in this case
00541          */
00542         sensor_cid = GetLastCid(data, data->shared->sid);
00543 
00544         snprintf(select_max_sensor_id, MAX_QUERY_LENGTH,
00545                  "SELECT MAX(cid) "
00546                  "  FROM event "
00547                  " WHERE sid = '%u'",
00548                  data->shared->sid);
00549         event_cid = Select(select_max_sensor_id, data);
00550 
00551         if ( event_cid > sensor_cid )
00552         {
00553            UpdateLastCid(data, data->shared->sid, event_cid);
00554            ErrorMessage("database: inconsistent cid information for sid=%u\n", 
00555                         data->shared->sid);
00556            ErrorMessage("          Recovering by rolling forward the cid=%u\n", 
00557                         event_cid);
00558         }
00559 
00560         data->shared->cid = event_cid;
00561         ++(data->shared->cid);
00562     }
00563     else
00564     {
00565         /* Free memory associated with data->shared */
00566         free(data->shared);
00567         data->shared = current->data;
00568     }
00569 
00570     /* free memory */
00571     free(select_sensor_id);      select_sensor_id = NULL;
00572     free(select_max_sensor_id);  select_max_sensor_id = NULL;
00573     free(insert_into_sensor);    insert_into_sensor = NULL;
00574     free(escapedSensorName);     escapedSensorName = NULL;
00575     free(escapedInterfaceName);  escapedInterfaceName = NULL;
00576 
00577     /* Get the versioning information for the DB schema */
00578     data->DBschema_version = CheckDBVersion(data);
00579     if( !pv.quiet_flag ) printf("database: schema version = %d\n", data->DBschema_version);
00580 
00581     if ( data->DBschema_version == 0 )
00582     {
00583        FatalError("database: The underlying database has not been initialized correctly.  This\n"
00584                   "          version of Snort requires version %d of the DB schema.  Your DB\n"
00585                   "          doesn't appear to have any records in the 'schema' table.\n"
00586                   "          Please re-run the appropriate DB creation script (e.g. create_mysql,\n"
00587                   "          create_postgresql, create_oracle, create_mssql) located in the\n"
00588                   "          contrib\\ directory.\n\n"
00589                   "          See the database documentation for cursory details (doc/README.database).\n"
00590                   "          and the URL to the most recent database plugin documentation.\n",
00591                   LATEST_DB_SCHEMA_VERSION);
00592     }
00593     if ( data->DBschema_version < LATEST_DB_SCHEMA_VERSION )
00594     {
00595        FatalError("database: The underlying database seems to be running an older version of\n"
00596                   "          the DB schema (current version=%d, required minimum version= %d).\n\n"
00597                   "          If you have an existing database with events logged by a previous\n"
00598                   "          version of snort, this database must first be upgraded to the latest\n"
00599                   "          schema (see the snort-users mailing list archive or DB plugin\n"
00600                   "          documention for details).\n\n"
00601                   "          If migrating old data is not desired, merely create a new instance\n"
00602                   "          of the snort database using the appropriate DB creation script\n"
00603                   "          (e.g. create_mysql, create_postgresql, create_oracle, create_mssql)\n"
00604                   "          located in the contrib\\ directory.\n\n"
00605                   "          See the database documentation for cursory details (doc/README.database).\n"
00606                   "          and the URL to the most recent database plugin documentation.\n",
00607                   data->DBschema_version, LATEST_DB_SCHEMA_VERSION);
00608     }
00609     /*
00610     else if ( data->DBschema_version < LATEST_DB_SCHEMA_VERSION )
00611     {                
00612        ErrorMessage("database: The database is using an older version of the DB schema\n");
00613     }
00614     */
00615 
00616     /* Add the processor function into the function list */
00617     if(!strncasecmp(data->facility,"log",3))
00618     {
00619         pv.log_plugin_active = 1;
00620         if( !pv.quiet_flag ) printf("database: using the \"log\" facility\n");
00621         AddFuncToOutputList(Database, NT_OUTPUT_LOG, data);
00622     }
00623     else
00624     {
00625         pv.alert_plugin_active = 1;
00626         if( !pv.quiet_flag ) printf("database: using the \"alert\" facility\n");
00627         AddFuncToOutputList(Database, NT_OUTPUT_ALERT, data);
00628     }
00629 
00630     AddFuncToCleanExitList(SpoDatabaseCleanExitFunction, data);
00631     AddFuncToRestartList(SpoDatabaseRestartFunction, data); 
00632     ++instances;
00633 }
00634 
00635 /*******************************************************************************
00636  * Function: ParseDatabaseArgs(char *)
00637  *
00638  * Purpose: Process the preprocessor arguements from the rules file and 
00639  *          initialize the preprocessor's data struct.
00640  *
00641  * Arguments: args => argument list
00642  *
00643  * Returns: void function
00644  *
00645  ******************************************************************************/
00646 DatabaseData *ParseDatabaseArgs(char *args)
00647 {
00648     DatabaseData *data;
00649     char *dbarg;
00650     char *a1;
00651     char *type;
00652     char *facility;
00653 
00654     data = (DatabaseData *)SnortAlloc(sizeof(DatabaseData));
00655     data->shared = (SharedDatabaseData *)SnortAlloc(sizeof(SharedDatabaseData));
00656 
00657     if(args == NULL)
00658     {
00659         ErrorMessage("database: you must supply arguments for database plugin\n");
00660         DatabasePrintUsage();
00661         FatalError("");
00662     }
00663 
00664     data->shared->dbtype_id = DB_UNDEFINED;
00665     data->sensor_name = NULL;
00666     data->facility = NULL;
00667     data->encoding = ENCODING_HEX;
00668     data->detail = DETAIL_FULL;
00669     data->ignore_bpf = 0;
00670 
00671     facility = strtok(args, ", ");
00672     if(facility != NULL)
00673     {
00674         if((!strncasecmp(facility,"log",3)) || (!strncasecmp(facility,"alert",5)))
00675             data->facility = facility;
00676         else
00677         {
00678             ErrorMessage("database: The first argument needs to be the logging facility\n");
00679             DatabasePrintUsage();
00680             FatalError("");
00681         }
00682     }
00683     else
00684     {
00685         ErrorMessage("database: Invalid format for first argment\n"); 
00686         DatabasePrintUsage();
00687         FatalError("");
00688     }
00689 
00690     type = strtok(NULL, ", ");
00691 
00692     if(type == NULL)
00693     {
00694         ErrorMessage("database: you must enter the database type in configuration file as the second argument\n");
00695         DatabasePrintUsage();
00696         FatalError("");
00697     }
00698 
00699     /* print out and test the capability of this plugin */
00700     if( !pv.quiet_flag ) printf("database: compiled support for ( ");
00701 
00702 
00703 #ifdef ENABLE_MYSQL
00704     if( !pv.quiet_flag ) printf("%s ",KEYWORD_MYSQL);
00705     if(!strncasecmp(type,KEYWORD_MYSQL,strlen(KEYWORD_MYSQL)))
00706         data->shared->dbtype_id = DB_MYSQL; 
00707 #endif
00708 #ifdef ENABLE_POSTGRESQL
00709     if( !pv.quiet_flag ) printf("%s ",KEYWORD_POSTGRESQL);
00710     if(!strncasecmp(type,KEYWORD_POSTGRESQL,strlen(KEYWORD_POSTGRESQL)))
00711         data->shared->dbtype_id = DB_POSTGRESQL; 
00712 #endif
00713 #ifdef ENABLE_ODBC
00714     if( !pv.quiet_flag ) printf("%s ",KEYWORD_ODBC);
00715     if(!strncasecmp(type,KEYWORD_ODBC,strlen(KEYWORD_ODBC)))
00716         data->shared->dbtype_id = DB_ODBC; 
00717 #endif
00718 #ifdef ENABLE_ORACLE
00719     if( !pv.quiet_flag ) printf("%s ",KEYWORD_ORACLE);
00720     if(!strncasecmp(type,KEYWORD_ORACLE,strlen(KEYWORD_ORACLE)))
00721         data->shared->dbtype_id = DB_ORACLE; 
00722 #endif
00723 #ifdef ENABLE_MSSQL
00724     if( !pv.quiet_flag ) printf("%s ",KEYWORD_MSSQL);
00725     if(!strncasecmp(type,KEYWORD_MSSQL,strlen(KEYWORD_MSSQL)))
00726         data->shared->dbtype_id = DB_MSSQL; 
00727 #endif
00728 
00729     if( !pv.quiet_flag ) printf(")\n");
00730 
00731     if( !pv.quiet_flag ) printf("database: configured to use %s\n", type);
00732 
00733     if(data->shared->dbtype_id == 0)
00734     {
00735         if ( !strncasecmp(type, KEYWORD_MYSQL, strlen(KEYWORD_MYSQL)) ||
00736              !strncasecmp(type, KEYWORD_POSTGRESQL, strlen(KEYWORD_POSTGRESQL)) ||
00737              !strncasecmp(type, KEYWORD_ODBC, strlen(KEYWORD_ODBC)) ||
00738              !strncasecmp(type, KEYWORD_MSSQL, strlen(KEYWORD_MSSQL))  ||
00739              !strncasecmp(type, KEYWORD_ORACLE, strlen(KEYWORD_ORACLE)) )
00740         {
00741             ErrorMessage("database: '%s' support is not compiled into this build of snort\n\n", type);
00742             FatalError("If this build of snort was obtained as a binary distribution (e.g., rpm,\n"
00743                        "or Windows), then check for alternate builds that contains the necessary\n"
00744                        "'%s' support.\n\n"
00745                        "If this build of snort was compiled by you, then re-run the\n"
00746                        "the ./configure script using the '--with-%s' switch.\n"
00747                        "For non-standard installations of a database, the '--with-%s=DIR'\n"
00748                        "syntax may need to be used to specify the base directory of the DB install.\n\n"
00749                        "See the database documentation for cursory details (doc/README.database).\n"
00750                        "and the URL to the most recent database plugin documentation.\n",
00751                        type, type, type);
00752         }
00753         else
00754         {
00755            FatalError("database: '%s' is an unknown database type.  The supported\n"
00756                       "          databases include: MySQL (mysql), PostgreSQL (postgresql),\n"
00757                       "          ODBC (odbc), Oracle (oracle), and Microsoft SQL Server (mssql)\n",
00758                       type);
00759         }
00760     }
00761 
00762     dbarg = strtok(NULL, " =");
00763     while(dbarg != NULL)
00764     {
00765         a1 = NULL;
00766         a1 = strtok(NULL, ", ");
00767         if(!strncasecmp(dbarg,KEYWORD_HOST,strlen(KEYWORD_HOST)))
00768         {
00769             data->shared->host = a1;
00770             if( !pv.quiet_flag ) printf("database:          host = %s\n", data->shared->host);
00771         }
00772         if(!strncasecmp(dbarg,KEYWORD_PORT,strlen(KEYWORD_PORT)))
00773         {
00774             data->port = a1;
00775             if( !pv.quiet_flag ) printf("database:          port = %s\n", data->port);
00776         }
00777         if(!strncasecmp(dbarg,KEYWORD_USER,strlen(KEYWORD_USER)))
00778         {
00779             data->user = a1;
00780             if( !pv.quiet_flag ) printf("database:          user = %s\n", data->user);
00781         }
00782         if(!strncasecmp(dbarg,KEYWORD_PASSWORD,strlen(KEYWORD_PASSWORD)))
00783         {
00784             if( !pv.quiet_flag ) printf("database: password is set\n");
00785             data->password = a1;
00786         }
00787         if(!strncasecmp(dbarg,KEYWORD_DBNAME,strlen(KEYWORD_DBNAME)))
00788         {
00789             data->shared->dbname = a1;
00790             if( !pv.quiet_flag ) printf("database: database name = %s\n", data->shared->dbname);
00791         }
00792         if(!strncasecmp(dbarg,KEYWORD_SENSORNAME,strlen(KEYWORD_SENSORNAME)))
00793         {
00794             data->sensor_name = a1;
00795             if( !pv.quiet_flag ) printf("database:   sensor name = %s\n", data->sensor_name);
00796         }
00797         if(!strncasecmp(dbarg,KEYWORD_ENCODING,strlen(KEYWORD_ENCODING)))
00798         {
00799             if(!strncasecmp(a1, KEYWORD_ENCODING_HEX, strlen(KEYWORD_ENCODING_HEX)))
00800             {
00801                 data->encoding = ENCODING_HEX;
00802             }
00803             else if(!strncasecmp(a1, KEYWORD_ENCODING_BASE64, strlen(KEYWORD_ENCODING_BASE64)))
00804             {
00805                 data->encoding = ENCODING_BASE64;
00806             }
00807             else if(!strncasecmp(a1, KEYWORD_ENCODING_ASCII, strlen(KEYWORD_ENCODING_ASCII)))
00808             {
00809                 data->encoding = ENCODING_ASCII;
00810             }
00811             else
00812             {
00813                 FatalError("database: unknown  (%s)", a1);
00814             }
00815             if( !pv.quiet_flag ) printf("database: data encoding = %s\n", a1);
00816         }
00817         if(!strncasecmp(dbarg,KEYWORD_DETAIL,strlen(KEYWORD_DETAIL)))
00818         {
00819             if(!strncasecmp(a1, KEYWORD_DETAIL_FULL, strlen(KEYWORD_DETAIL_FULL)))
00820             {
00821                 data->detail = DETAIL_FULL;
00822             }
00823             else if(!strncasecmp(a1, KEYWORD_DETAIL_FAST, strlen(KEYWORD_DETAIL_FAST)))
00824             {
00825                 data->detail = DETAIL_FAST;
00826             }
00827             else
00828             {
00829                 FatalError("database: unknown detail level (%s)", a1);
00830             } 
00831             if( !pv.quiet_flag ) printf("database: detail level  = %s\n", a1);
00832         }
00833         if(!strncasecmp(dbarg,KEYWORD_IGNOREBPF,strlen(KEYWORD_IGNOREBPF)))
00834         {
00835             if(!strncasecmp(a1, KEYWORD_IGNOREBPF_NO, strlen(KEYWORD_IGNOREBPF_NO)) ||
00836                !strncasecmp(a1, KEYWORD_IGNOREBPF_ZERO, strlen(KEYWORD_IGNOREBPF_ZERO)))
00837             {
00838                 data->ignore_bpf = 0;
00839             }
00840             else if(!strncasecmp(a1, KEYWORD_IGNOREBPF_YES, strlen(KEYWORD_IGNOREBPF_YES)) ||
00841                     !strncasecmp(a1, KEYWORD_IGNOREBPF_ONE, strlen(KEYWORD_IGNOREBPF_ONE)))
00842             {
00843                 data->ignore_bpf = 1;
00844             }
00845             else
00846             {
00847                 FatalError("database: unknown ignore_bpf argument (%s)", a1);
00848             }
00849 
00850             if( !pv.quiet_flag ) printf("database: ignore_bpf = %s\n", a1);
00851         }
00852         dbarg = strtok(NULL, "=");
00853     } 
00854 
00855     if(data->shared->dbname == NULL)
00856     {
00857         ErrorMessage("database: must enter database name in configuration file\n\n");
00858         DatabasePrintUsage();
00859         FatalError("");
00860     }
00861 
00862     return data;
00863 }
00864 
00865 void FreeQueryNode(SQLQuery * node)
00866 {
00867     if(node)
00868     {
00869         FreeQueryNode(node->next);
00870         node->next = NULL;
00871         free(node->val);
00872         node->val = NULL;
00873         free(node);
00874     }
00875 }
00876 
00877 SQLQuery * NewQueryNode(SQLQuery * parent, int query_size)
00878 {
00879     SQLQuery * rval;
00880 
00881     if(query_size == 0)
00882     {
00883         query_size = MAX_QUERY_LENGTH;
00884     }
00885 
00886     if(parent)
00887     {
00888         while(parent->next)
00889         {
00890             parent = parent->next;
00891         }
00892 
00893         parent->next = (SQLQuery *)SnortAlloc(sizeof(SQLQuery));
00894         rval = parent->next;
00895     }
00896     else
00897     {
00898         rval = (SQLQuery *)SnortAlloc(sizeof(SQLQuery));
00899     }
00900 
00901     rval->val = (char *)SnortAlloc(query_size);
00902     rval->next = NULL;
00903 
00904     return rval;
00905 }  
00906 
00907 /*******************************************************************************
00908  * Function: Database(Packet *, char * msg, void *arg)
00909  *
00910  * Purpose: Insert data into the database
00911  *
00912  * Arguments: p   => pointer to the current packet data struct 
00913  *            msg => pointer to the signature message
00914  *
00915  * Returns: void function
00916  *
00917  ******************************************************************************/
00918 void Database(Packet *p, char *msg, void *arg, Event *event)
00919 {
00920     DatabaseData *data = (DatabaseData *)arg;
00921     SQLQuery * query;
00922     SQLQuery * root;
00923     char *timestamp_string
00924        , *insert_fields
00925        , *insert_values
00926        , *sig_name
00927        , *sig_class
00928        , *ref_system_name
00929        , *ref_node_id_string
00930        , *ref_tag
00931        , *packet_data
00932        , *packet_data_not_escaped;
00933     int  i
00934        , insert_fields_len
00935        , insert_values_len
00936        , ok_transaction;
00937     char *select0 = NULL,
00938          *select1 = NULL,
00939          *insert0 = NULL;
00940     unsigned int sig_id;
00941     int ref_system_id;
00942     unsigned int ref_id, class_id=0;
00943     ClassType *class_ptr;
00944     ReferenceNode *refNode;
00945 
00946     query = NewQueryNode(NULL, 0);
00947     root = query;
00948 
00949 #ifdef ENABLE_DB_TRANSACTIONS
00950     BeginTransaction(data);
00951 #endif
00952     
00953     if(msg == NULL)
00954     {
00955         msg = "";
00956     }
00957 
00958     /*** Build the query for the Event Table ***/
00959 
00960     /* Generate a default-formatted timestamp now */
00961     if(p != NULL)
00962     {
00963         timestamp_string = GetTimestamp((struct timeval *) &p->pkth->ts, data->tz);
00964     }
00965     else
00966     {
00967         timestamp_string = GetCurrentTimestamp();
00968     }
00969 
00970 #ifdef ENABLE_MSSQL
00971     if(data->shared->dbtype_id == DB_MSSQL)
00972     {
00973         /* SQL Server uses a date format which is slightly
00974          * different from the ISO-8601 standard generated
00975          * by GetTimestamp() and GetCurrentTimestamp().  We
00976          * need to convert from the ISO-8601 format of:
00977          *   "1998-01-25 23:59:59+14316557"
00978          * to the SQL Server format of:
00979          *   "1998-01-25 23:59:59.143"
00980          */
00981         if( timestamp_string!=NULL && strlen(timestamp_string)>20 )
00982         {
00983             timestamp_string[19] = '.';
00984         }
00985         if( timestamp_string!=NULL && strlen(timestamp_string)>24 )
00986         {
00987             timestamp_string[23] = '\0';
00988         }
00989     }
00990 #endif
00991 #ifdef ENABLE_ORACLE
00992     if (data->shared->dbtype_id == DB_ORACLE)
00993     {
00994         /* Oracle (everything before 9i) does not support
00995          * date information smaller than 1 second.
00996          * To go along with the TO_DATE() Oracle function
00997          * below, this was written to strip out all the
00998          * excess information. (everything beyond a second)
00999          * Use the Oracle format of:
01000          *   "1998-01-25 23:59:59"
01001          */
01002         if ( timestamp_string!=NULL && strlen(timestamp_string)>20 )
01003         {
01004             timestamp_string[19] = '\0';
01005         }
01006     }
01007 #endif
01008 #ifdef ENABLE_ODBC
01009     if (data->shared->dbtype_id == DB_ODBC)
01010     {
01011         /* ODBC defines escape sequences for date data.
01012          * These escape sequences are of the format:
01013          *   {literal-type 'value'}
01014          * The Timestamp (ts) escape sequence handles
01015          * date/time values of the format:
01016          *   yyyy-mm-dd hh:mm:ss[.f...]
01017          * where the number of digits to the right of the
01018          * decimal point in a time or timestamp interval
01019          * literal containing a seconds component is
01020          * dependent on the seconds precision, as contained
01021          * in the SQL_DESC_PRECISION descriptor field. (For
01022          * more information, see function SQLSetDescField.)
01023          *
01024          * The number of decimal places within the fraction
01025          * of a second is database dependant.  I wasn't able
01026          * to easily determine the granularity of this
01027          * value using SQL_DESC_PRECISION, so choosing to
01028          * simply discard the fractional part.
01029          */
01030         if( timestamp_string!=NULL && strlen(timestamp_string)>20 )
01031         {
01032             timestamp_string[19] = '\0';
01033         }
01034     }
01035 #endif
01036 #ifdef ENABLE_POSTGRESQL
01037     if( data->shared->dbtype_id == DB_POSTGRESQL ){
01038         /* From Posgres Documentation
01039          * For timestamp with time zone, the internally stored
01040          * value is always in UTC (GMT). An input value that has
01041          * an explicit time zone specified is converted to UTC
01042          * using the appropriate offset for that time zone. If no
01043          * time zone is stated in the input string, then it is assumed
01044          * to be in the time zone indicated by the system's TimeZone
01045          * parameter, and is converted to UTC using the offset for
01046          * the TimeZone zone
01047          */
01048         if( timestamp_string!=NULL && strlen(timestamp_string)>24 )
01049         {
01050             timestamp_string[23] = '\0';
01051         }
01052     }
01053 #endif
01054 
01055     /* Write the signature information 
01056      *  - Determine the ID # of the signature of this alert 
01057      */
01058     select0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01059     sig_name = snort_escape_string(msg, data);
01060     if ( event->sig_rev == 0 ) 
01061     {
01062         if( event->sig_id == 0) 
01063         {
01064             snprintf(select0, MAX_QUERY_LENGTH, 
01065                     "SELECT sig_id "
01066                     "  FROM signature "
01067                     " WHERE sig_name = '%s' "
01068                     "   AND sig_rev IS NULL "
01069                     "   AND sig_sid IS NULL ",
01070                     sig_name);
01071         }
01072         else 
01073         {
01074             snprintf(select0, MAX_QUERY_LENGTH, 
01075                     "SELECT sig_id "
01076                     "  FROM signature "
01077                     " WHERE sig_name = '%s' "
01078                     "   AND sig_rev IS NULL "
01079                     "   AND sig_sid = %u ", 
01080                     sig_name,
01081                     event->sig_id);
01082         }
01083     }
01084     else
01085     {
01086         if( event->sig_id == 0)
01087         {
01088             snprintf(select0, MAX_QUERY_LENGTH,
01089                     "SELECT sig_id "
01090                     "  FROM signature "
01091                     " WHERE sig_name = '%s' "
01092                     "   AND sig_rev = %u "
01093                     "   AND sig_sid IS NULL ",
01094                     sig_name,
01095                     event->sig_rev);
01096         }
01097         else
01098         {
01099             snprintf(select0, MAX_QUERY_LENGTH,
01100                     "SELECT sig_id "
01101                     "  FROM signature "
01102                     " WHERE sig_name = '%s' "
01103                     "   AND sig_rev = %u "
01104                     "   AND sig_sid = %u ",
01105                     sig_name,
01106                     event->sig_rev,
01107                     event->sig_id);
01108         }
01109     }
01110     
01111     sig_id = Select(select0, data);
01112 
01113     /* If this signature is detected for the first time
01114      *  - write the signature
01115      *  - write the signature's references, classification, priority, id,
01116      *                          revision number
01117      * Note: if a signature (identified with a unique text message, revision #) 
01118      *       initially is logged to the DB without references/classification, 
01119      *       but later they are added, this information will _not_ be 
01120      *       stored/updated unless the revision number is changed.
01121      *       This algorithm is used in order to prevent many DB SELECTs to
01122      *       verify their presence _every_ time the alert is triggered. 
01123      */
01124     if(sig_id == 0)
01125     {
01126         /* get classification and priority information  */
01127         if(otn_tmp)
01128         {
01129             class_ptr = otn_tmp->sigInfo.classType;
01130 
01131             if(class_ptr)
01132             {
01133                 /* classification */
01134                 if(class_ptr->type)
01135                 {
01136                     /* Get the ID # of this classification */ 
01137                     select1 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01138                     sig_class = snort_escape_string(class_ptr->type, data);
01139             
01140                     snprintf(select1, MAX_QUERY_LENGTH, 
01141                             "SELECT sig_class_id "
01142                             "  FROM sig_class "
01143                             " WHERE sig_class_name = '%s'",
01144                             sig_class);
01145                     class_id = Select(select1, data);
01146 
01147                     if ( !class_id )
01148                     {
01149                         insert0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01150                         snprintf(insert0, MAX_QUERY_LENGTH,
01151                                 "INSERT INTO "
01152                                 "sig_class (sig_class_name) "
01153                                 "VALUES ('%s')",
01154                                 sig_class);
01155                         Insert(insert0, data);
01156                         free(insert0);    insert0 = NULL;
01157                         class_id = Select(select1, data);
01158                         if ( !class_id )
01159                         {
01160                             ErrorMessage("database: unable to write classification\n");
01161                         }
01162                     }
01163                     free(select1);    select1 = NULL;
01164                     free(sig_class);  sig_class = NULL;
01165                 }
01166             }
01167         }
01168 
01169         insert0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01170         insert_fields = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01171         insert_values = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01172         insert_fields_len = 0;
01173         insert_values_len = 0;
01174 
01175         snprintf(insert_fields, MAX_QUERY_LENGTH-insert_fields_len, "%s", "sig_name");
01176         snprintf(insert_values, MAX_QUERY_LENGTH-insert_values_len, "'%s'", sig_name);
01177         insert_fields_len = strlen(insert_fields);
01178         insert_values_len = strlen(insert_values);
01179 
01180         if ( class_id > 0 )
01181         {
01182             snprintf(&insert_fields[insert_fields_len], MAX_QUERY_LENGTH-insert_fields_len, "%s", ",sig_class_id");
01183             snprintf(&insert_values[insert_values_len], MAX_QUERY_LENGTH-insert_values_len, ",%u", class_id);
01184             insert_fields_len = strlen(insert_fields);
01185             insert_values_len = strlen(insert_values);
01186         } 
01187 
01188         if ( event->priority > 0 )
01189         {
01190             snprintf(&insert_fields[insert_fields_len], MAX_QUERY_LENGTH-insert_fields_len, "%s", ",sig_priority");
01191             snprintf(&insert_values[insert_values_len], MAX_QUERY_LENGTH-insert_values_len, ",%u", event->priority);
01192             insert_fields_len = strlen(insert_fields);
01193             insert_values_len = strlen(insert_values);
01194         }
01195 
01196         if ( event->sig_rev > 0 )
01197         {
01198             snprintf(&insert_fields[insert_fields_len], MAX_QUERY_LENGTH-insert_fields_len, "%s", ",sig_rev");
01199             snprintf(&insert_values[insert_values_len], MAX_QUERY_LENGTH-insert_values_len, ",%u", event->sig_rev);
01200             insert_fields_len = strlen(insert_fields);
01201             insert_values_len = strlen(insert_values);
01202         }
01203 
01204         if ( event->sig_id > 0 )
01205         {
01206             snprintf(&insert_fields[insert_fields_len], MAX_QUERY_LENGTH-insert_fields_len, "%s", ",sig_sid");
01207             snprintf(&insert_values[insert_values_len], MAX_QUERY_LENGTH-insert_values_len, ",%u", event->sig_id);
01208             insert_fields_len = strlen(insert_fields);
01209             insert_values_len = strlen(insert_values);            
01210         }
01211 
01212         snprintf(insert0, MAX_QUERY_LENGTH,
01213                 "INSERT INTO signature (%s) VALUES (%s)",
01214                 insert_fields, insert_values);
01215 
01216         Insert(insert0,data);
01217 
01218         free(insert0);             insert0 = NULL;
01219         free(insert_fields);       insert_fields = NULL;
01220         free(insert_values);       insert_values = NULL;
01221 
01222         sig_id = Select(select0,data);
01223         if(sig_id == 0)
01224         {
01225             ErrorMessage("database: Problem inserting a new signature '%s'\n", msg);
01226         }
01227         free(select0);    select0 = NULL;
01228 
01229         /* add the external rule references  */
01230         if(otn_tmp)
01231         {
01232             refNode = otn_tmp->sigInfo.refs;
01233             i = 1;
01234 
01235             while(refNode)
01236             {
01237                 /* Get the ID # of the reference from the DB */
01238                 select0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01239                 insert0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01240                 ref_system_name = snort_escape_string(refNode->system->name, data);
01241         
01242                 /* Note: There is an underlying assumption that the SELECT
01243                  *       will do a case-insensitive comparison.
01244                  */
01245                 snprintf(select0, MAX_QUERY_LENGTH, 
01246                         "SELECT ref_system_id "
01247                         "  FROM reference_system "
01248                         " WHERE ref_system_name = '%s'",
01249                         ref_system_name);
01250                 snprintf(insert0, MAX_QUERY_LENGTH,
01251                         "INSERT INTO "
01252                         "reference_system (ref_system_name) "
01253                         "VALUES ('%s')",
01254                         ref_system_name);
01255                 ref_system_id = Select(select0, data);
01256                 if ( ref_system_id == 0 )
01257                 {
01258                     Insert(insert0, data);
01259                     ref_system_id = Select(select0, data);
01260                 }
01261 
01262                 free(select0);            select0 = NULL;
01263                 free(insert0);            insert0 = NULL;
01264                 free(ref_system_name);    ref_system_name = NULL;
01265 
01266                 if ( ref_system_id > 0 )
01267                 {
01268                     select0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01269                     ref_tag = snort_escape_string(refNode->id, data);
01270                     snprintf(select0, MAX_QUERY_LENGTH,
01271                             "SELECT ref_id "
01272                             "  FROM reference "
01273                             " WHERE ref_system_id = %d "
01274                             "   AND ref_tag = '%s'",
01275                             ref_system_id, ref_tag);
01276                     ref_id = Select(select0, data);
01277                     free(ref_tag);    ref_tag = NULL;
01278             
01279                     /* If this reference is not in the database, write it */
01280                     if ( ref_id == 0 )
01281                     {
01282                         /* truncate the reference tag as necessary */
01283                         ref_node_id_string = (char *) SnortAlloc(101);
01284                         if ( data->DBschema_version == 103 )
01285                         {
01286                             snprintf(ref_node_id_string, 20, "%s", refNode->id);
01287                         }
01288                         else if ( data->DBschema_version >= 104 )
01289                         {
01290                             snprintf(ref_node_id_string, 100, "%s", refNode->id);
01291                         }
01292 
01293                         insert0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01294                         ref_tag = snort_escape_string(ref_node_id_string, data);
01295                         snprintf(insert0, MAX_QUERY_LENGTH,
01296                                 "INSERT INTO "
01297                                 "reference (ref_system_id, ref_tag) "
01298                                 "VALUES (%d, '%s')",
01299                                 ref_system_id, ref_tag);
01300                         Insert(insert0, data);
01301                         ref_id = Select(select0, data);
01302                         free(insert0);               insert0 = NULL;
01303                         free(ref_node_id_string);    ref_node_id_string = NULL;
01304                         free(ref_tag);               ref_tag = NULL;
01305 
01306                         if ( ref_id == 0 )
01307                         {
01308                             ErrorMessage("database: Unable to insert the alert reference into the DB\n");
01309                         }
01310                     }
01311                     free(select0);    select0 = NULL;
01312 
01313                     insert0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01314                     snprintf(insert0, MAX_QUERY_LENGTH,
01315                             "INSERT INTO "
01316                             "sig_reference (sig_id, ref_seq, ref_id) "
01317                             "VALUES (%u, %d, %u)",
01318                             sig_id, i, ref_id);
01319                     Insert(insert0, data);
01320                     free(insert0);    insert0 = NULL;
01321                     ++i;
01322                 }
01323                 else
01324                 {
01325                     ErrorMessage("database: Unable to insert unknown reference tag ('%s') used in rule.\n", refNode->id);
01326                 }
01327 
01328                 refNode = refNode->next;
01329             }
01330         }
01331     }
01332     else
01333     {
01334         free(select0);    select0 = NULL;
01335     }
01336 
01337     free(sig_name);    sig_name = NULL;
01338     
01339     if ( (data->shared->dbtype_id == DB_ORACLE) &&
01340          (data->DBschema_version >= 105) )
01341     {
01342         snprintf(query->val, MAX_QUERY_LENGTH,
01343                 "INSERT INTO "
01344                 "event (sid,cid,signature,timestamp) "
01345                 "VALUES ('%u', '%u', '%u', TO_DATE('%s', 'YYYY-MM-DD HH24:MI:SS'))",
01346                 data->shared->sid, data->shared->cid, sig_id, timestamp_string);
01347     }
01348     else if(data->shared->dbtype_id == DB_ODBC)
01349     {
01350         snprintf(query->val, MAX_QUERY_LENGTH,
01351                 "INSERT INTO "
01352                 "event (sid,cid,signature,timestamp) "
01353                 "VALUES ('%u', '%u', '%u', {ts '%s'})",
01354                 data->shared->sid, data->shared->cid, sig_id, timestamp_string);
01355     }
01356     else
01357     {
01358         snprintf(query->val, MAX_QUERY_LENGTH,
01359                 "INSERT INTO "
01360                 "event (sid,cid,signature,timestamp) "
01361                 "VALUES ('%u', '%u', '%u', '%s')",
01362                 data->shared->sid, data->shared->cid, sig_id, timestamp_string);
01363     }
01364 
01365     free(timestamp_string);    timestamp_string = NULL;
01366 
01367     /* We do not log fragments! They are assumed to be handled 
01368        by the fragment reassembly pre-processor */
01369 
01370     if(p != NULL)
01371     {
01372         if((!p->frag_flag) && (p->iph)) 
01373         {
01374             /* query = NewQueryNode(query, 0); */
01375             if(p->iph->ip_proto == IPPROTO_ICMP && p->icmph)
01376             {
01377                 query = NewQueryNode(query, 0);
01378                 /*** Build a query for the ICMP Header ***/
01379                 if(data->detail)
01380                 {
01381                     if(p->icmph)
01382                     {
01383                         snprintf(query->val, MAX_QUERY_LENGTH, 
01384                                 "INSERT INTO "
01385                                 "icmphdr (sid, cid, icmp_type, icmp_code, icmp_csum, icmp_id, icmp_seq) "
01386                                 "VALUES ('%u','%u','%u','%u','%u','%u','%u')",
01387                                 data->shared->sid,
01388                                 data->shared->cid,
01389                                 p->icmph->type,
01390                                 p->icmph->code,
01391                                 ntohs(p->icmph->csum),
01392                                 ntohs(p->icmph->s_icmp_id),
01393                                 ntohs(p->icmph->s_icmp_seq));
01394                     }
01395                     else
01396                     {
01397                         snprintf(query->val, MAX_QUERY_LENGTH, 
01398                                 "INSERT INTO "
01399                                 "icmphdr (sid, cid, icmp_type, icmp_code, icmp_csum) "
01400                                 "VALUES ('%u','%u','%u','%u','%u')",
01401                                 data->shared->sid,
01402                                 data->shared->cid,
01403                                 p->icmph->type,
01404                                 p->icmph->code,
01405                                 ntohs(p->icmph->csum));
01406                     }
01407                 }
01408                 else
01409                 {
01410                     snprintf(query->val, MAX_QUERY_LENGTH, 
01411                             "INSERT INTO "
01412                             "icmphdr (sid, cid, icmp_type, icmp_code) "
01413                             "VALUES ('%u','%u','%u','%u')",
01414                             data->shared->sid,
01415                             data->shared->cid,
01416                             p->icmph->type,
01417                             p->icmph->code);
01418                 }
01419             }
01420             else if(p->iph->ip_proto == IPPROTO_TCP && p->tcph)
01421             {
01422                 query = NewQueryNode(query, 0);
01423                 /*** Build a query for the TCP Header ***/
01424                 if(data->detail)
01425                 {
01426                     snprintf(query->val, MAX_QUERY_LENGTH, 
01427                             "INSERT INTO "
01428                             "tcphdr (sid, cid, tcp_sport, tcp_dport, "
01429                             "        tcp_seq, tcp_ack, tcp_off, tcp_res, "
01430                             "        tcp_flags, tcp_win, tcp_csum, tcp_urp) "
01431                             "VALUES ('%u','%u','%u','%u','%lu','%lu','%u','%u','%u','%u','%u','%u')",
01432                             data->shared->sid,
01433                             data->shared->cid,
01434                             ntohs(p->tcph->th_sport), 
01435                             ntohs(p->tcph->th_dport),
01436                             (u_long)ntohl(p->tcph->th_seq),
01437                             (u_long)ntohl(p->tcph->th_ack),
01438                             TCP_OFFSET(p->tcph), 
01439                             TCP_X2(p->tcph),
01440                             p->tcph->th_flags, 
01441                             ntohs(p->tcph->th_win),
01442                             ntohs(p->tcph->th_sum),
01443                             ntohs(p->tcph->th_urp));
01444                 }
01445                 else
01446                 {
01447                     snprintf(query->val, MAX_QUERY_LENGTH, 
01448                             "INSERT INTO "
01449                             "tcphdr (sid,cid,tcp_sport,tcp_dport,tcp_flags) "
01450                             "VALUES ('%u','%u','%u','%u','%u')",
01451                             data->shared->sid,
01452                             data->shared->cid,
01453                             ntohs(p->tcph->th_sport), 
01454                             ntohs(p->tcph->th_dport),
01455                             p->tcph->th_flags);
01456                 }
01457 
01458 
01459                 if(data->detail)
01460                 {
01461                     /*** Build the query for TCP Options ***/
01462                     for(i=0; i < (int)(p->tcp_option_count); i++)
01463                     {
01464                         query = NewQueryNode(query, 0);
01465                         if((data->encoding == ENCODING_HEX) || (data->encoding == ENCODING_ASCII))
01466                         {
01467                             packet_data = fasthex(p->tcp_options[i].data, p->tcp_options[i].len); 
01468                         }
01469                         else
01470                         {
01471                             packet_data = base64(p->tcp_options[i].data, p->tcp_options[i].len);
01472                         }
01473                         snprintf(query->val, MAX_QUERY_LENGTH, 
01474                                 "INSERT INTO "
01475                                 "opt (sid,cid,optid,opt_proto,opt_code,opt_len,opt_data) "
01476                                 "VALUES ('%u','%u','%u','%u','%u','%u','%s')",
01477                                 data->shared->sid,
01478                                 data->shared->cid,
01479                                 i,
01480                                 6,
01481                                 p->tcp_options[i].code,
01482                                 p->tcp_options[i].len,
01483                                 packet_data); 
01484                         free(packet_data);    packet_data = NULL;
01485                     }
01486                 }
01487             }
01488             else if(p->iph->ip_proto == IPPROTO_UDP && p->udph)
01489             {
01490                 query = NewQueryNode(query, 0);
01491                 /*** Build the query for the UDP Header ***/
01492                 if(data->detail)
01493                 {
01494                     snprintf(query->val, MAX_QUERY_LENGTH,
01495                             "INSERT INTO "
01496                             "udphdr (sid, cid, udp_sport, udp_dport, udp_len, udp_csum) "
01497                             "VALUES ('%u', '%u', '%u', '%u', '%u', '%u')",
01498                             data->shared->sid,
01499                             data->shared->cid,
01500                             ntohs(p->udph->uh_sport), 
01501                             ntohs(p->udph->uh_dport),
01502                             ntohs(p->udph->uh_len),
01503                             ntohs(p->udph->uh_chk));
01504                 }
01505                 else
01506                 {
01507                     snprintf(query->val, MAX_QUERY_LENGTH,
01508                             "INSERT INTO "
01509                             "udphdr (sid, cid, udp_sport, udp_dport) "
01510                             "VALUES ('%u', '%u', '%u', '%u')",
01511                             data->shared->sid,
01512                             data->shared->cid,
01513                             ntohs(p->udph->uh_sport), 
01514                             ntohs(p->udph->uh_dport));
01515                 }
01516             }
01517         }   
01518 
01519         /*** Build the query for the IP Header ***/
01520         if ( p->iph )
01521         {
01522             query = NewQueryNode(query, 0);
01523 
01524             if(data->detail)
01525             {
01526                 snprintf(query->val, MAX_QUERY_LENGTH, 
01527                         "INSERT INTO "
01528                         "iphdr (sid, cid, ip_src, ip_dst, ip_ver, ip_hlen, "
01529                         "       ip_tos, ip_len, ip_id, ip_flags, ip_off,"
01530                         "       ip_ttl, ip_proto, ip_csum) "
01531                         "VALUES ('%u','%u','%lu','%lu','%u','%u','%u','%u','%u','%u','%u','%u','%u','%u')",
01532                         data->shared->sid,
01533                         data->shared->cid,
01534                         (u_long)ntohl(p->iph->ip_src.s_addr), 
01535                         (u_long)ntohl(p->iph->ip_dst.s_addr), 
01536                         IP_VER(p->iph),
01537                         IP_HLEN(p->iph), 
01538                         p->iph->ip_tos,
01539                         ntohs(p->iph->ip_len),
01540                         ntohs(p->iph->ip_id), 
01541                         p->frag_flag,
01542                         ntohs(p->frag_offset),
01543                         p->iph->ip_ttl, 
01544                         p->iph->ip_proto,
01545                         ntohs(p->iph->ip_csum));
01546             }
01547             else
01548             {
01549                 snprintf(query->val, MAX_QUERY_LENGTH, 
01550 
01551                         "INSERT INTO "
01552                         "iphdr (sid, cid, ip_src, ip_dst, ip_proto) "
01553                         "VALUES ('%u','%u','%lu','%lu','%u')",
01554                         data->shared->sid,
01555                         data->shared->cid,
01556                         (u_long)ntohl(p->iph->ip_src.s_addr),
01557                         (u_long)ntohl(p->iph->ip_dst.s_addr),
01558                         p->iph->ip_proto);
01559             }
01560 
01561             /*** Build querys for the IP Options ***/
01562             if(data->detail)
01563             {
01564                 for(i=0 ; i < (int)(p->ip_option_count); i++)
01565                 {
01566                     if(&p->ip_options[i])
01567                     {
01568                         query = NewQueryNode(query, 0);
01569                         if((data->encoding == ENCODING_HEX) || (data->encoding == ENCODING_ASCII))
01570                         {
01571                             packet_data = fasthex(p->ip_options[i].data, p->ip_options[i].len); 
01572                         }
01573                         else
01574                         {
01575                             packet_data = base64(p->ip_options[i].data, p->ip_options[i].len); 
01576                         }
01577 
01578                         snprintf(query->val, MAX_QUERY_LENGTH, 
01579                                 "INSERT INTO "
01580                                 "opt (sid,cid,optid,opt_proto,opt_code,opt_len,opt_data) "
01581                                 "VALUES ('%u','%u','%u','%u','%u','%u','%s')",
01582                                 data->shared->sid,
01583                                 data->shared->cid,
01584                                 i,
01585                                 0,
01586                                 p->ip_options[i].code,
01587                                 p->ip_options[i].len,
01588                                 packet_data); 
01589                         free(packet_data);    packet_data = NULL;
01590                     }
01591                 }
01592             }
01593         }
01594 
01595         /*** Build query for the payload ***/
01596         if ( p->data )
01597         {
01598             if(data->detail)
01599             {
01600                 if(p->dsize)
01601                 {
01602                     query = NewQueryNode(query, p->dsize * 2 + MAX_QUERY_LENGTH);
01603                     memset(query->val, 0, p->dsize*2 + MAX_QUERY_LENGTH);
01604                     if(data->encoding == ENCODING_BASE64)
01605                     {
01606                         packet_data_not_escaped = base64(p->data, p->dsize);
01607                     }
01608                     else if(data->encoding == ENCODING_ASCII)
01609                     {
01610                         packet_data_not_escaped = ascii(p->data, p->dsize);
01611                     }
01612                     else
01613                     {
01614                         packet_data_not_escaped = fasthex(p->data, p->dsize);
01615                     }
01616 
01617                     packet_data = snort_escape_string(packet_data_not_escaped, data);
01618 
01619                     if(data->shared->dbtype_id == DB_ORACLE)
01620                     {                    
01621                         snprintf(query->val, (p->dsize * 2) + MAX_QUERY_LENGTH - 3, 
01622                                 "INSERT INTO "
01623                                 "data (sid,cid,data_payload) "
01624                                 "VALUES ('%u','%u',utl_raw.cast_to_raw('%s",
01625                                 data->shared->sid,
01626                                 data->shared->cid,
01627                                 packet_data);
01628                         strcat(query->val, "'))");
01629                         free (packet_data);                packet_data = NULL;
01630                         free (packet_data_not_escaped);    packet_data_not_escaped = NULL;
01631                     }
01632                     else
01633                     {
01634                         snprintf(query->val, (p->dsize * 2) + MAX_QUERY_LENGTH - 3,
01635                                 "INSERT INTO "
01636                                 "data (sid,cid,data_payload) "
01637                                 "VALUES ('%u','%u','%s",
01638                         data->shared->sid,
01639                         data->shared->cid,
01640                         packet_data);
01641                         strcat(query->val, "')");
01642                         free (packet_data);                packet_data = NULL;
01643                         free (packet_data_not_escaped);    packet_data_not_escaped = NULL;
01644                     }
01645                 }
01646             }
01647         }
01648     }
01649 
01650     /* Execute the queries */
01651     query = root;
01652     ok_transaction = 1;
01653     while(query)
01654     {
01655         if ( Insert(query->val,data) == 0 )
01656         {
01657 #ifdef ENABLE_DB_TRANSACTIONS
01658            RollbackTransaction(data);
01659 #endif
01660            ok_transaction = 0;
01661            break;
01662         }
01663         else
01664         {
01665            query = query->next;
01666         }
01667     }
01668     FreeQueryNode(root); 
01669     root = NULL;
01670 
01671     /* Increment the cid*/
01672     data->shared->cid++;
01673 
01674 #ifdef ENABLE_DB_TRANSACTIONS
01675     if ( ok_transaction )
01676     {
01677        CommitTransaction(data);
01678     }
01679 #endif
01680     
01681     /* An ODBC bugfix */
01682 #ifdef ENABLE_ODBC
01683     if(data->shared->cid == 600)
01684     {
01685         data->shared->cid = 601;
01686     }
01687 #endif
01688 }
01689 
01690 /* Some of the code in this function is from the 
01691    mysql_real_escape_string() function distributed with mysql.
01692 
01693    Those portions of this function remain
01694    Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
01695 
01696    We needed a more general case that was not MySQL specific so there
01697    were small modifications made to the mysql_real_escape_string() 
01698    function. */
01699 
01700 char * snort_escape_string(char * from, DatabaseData * data)
01701 {
01702     char * to;
01703     char * to_start;
01704     char * end; 
01705     int from_length;
01706 
01707     from_length = (int)strlen(from);
01708 
01709     to = (char *)SnortAlloc(strlen(from) * 2 + 1);
01710     to_start = to;
01711 #ifdef ENABLE_ORACLE
01712     if (data->shared->dbtype_id == DB_ORACLE)
01713     {
01714       for (end=from+from_length; from != end; from++)
01715       {
01716         switch(*from)
01717         {
01718           case '\'':           /*  '  -->  '' */
01719             *to++= '\'';
01720             *to++= '\'';
01721             break;
01722           case '\032':         /* Ctrl-Z (Win32 EOF)  -->  \\Z */
01723             *to++= '\\';       /* This gives problems on Win32 */
01724             *to++= 'Z';
01725             break;
01726           default:             /* copy character directly */
01727             *to++= *from;
01728         }
01729       }
01730     }
01731     else
01732 #endif
01733 #ifdef ENABLE_MSSQL
01734     if (data->shared->dbtype_id == DB_MSSQL)
01735     {
01736       for (end=from+from_length; from != end; from++)
01737       {
01738         switch(*from)
01739         {
01740           case '\'':           /*  '  -->  '' */      
01741             *to++= '\'';
01742             *to++= '\'';
01743             break;
01744           default:             /* copy character directly */
01745             *to++= *from;
01746         }
01747       }
01748     }
01749     else
01750 #endif
01751 /* Historically these were together in a common "else".
01752  * Keeping it that way until somebody complains...
01753  */
01754 #if defined(ENABLE_MYSQL) || defined(ENABLE_POSTGRESQL)
01755     if (data->shared->dbtype_id == DB_MYSQL ||
01756         data->shared->dbtype_id == DB_POSTGRESQL)
01757     {
01758       for(end=from+from_length; from != end; from++)
01759       {
01760         switch(*from)
01761         {
01762           /*
01763            * Only need to escape '%' and '_' characters
01764            * when querying a SELECT...LIKE, which never
01765            * occurs in Snort.  Excluding these checks
01766            * for that reason.
01767           case '%':            ** %  -->  \% **
01768             *to++= '\\';
01769             *to++= '%';
01770             break;
01771           case '_':            ** _  -->  \_ **
01772             *to++= '\\';
01773             *to++= '_';
01774             break;
01775            */
01776 
01777           case 0:              /* NULL  -->  \\0  (probably never encountered due to strlen() above) */
01778             *to++= '\\';       /* Must be escaped for 'mysql' */
01779             *to++= '0';
01780             break;
01781           case '\n':           /* \n  -->  \\n */
01782             *to++= '\\';       /* Must be escaped for logs */
01783             *to++= 'n';
01784             break;
01785           case '\r':           /* \r  -->  \\r */
01786             *to++= '\\';
01787             *to++= 'r';
01788             break;
01789           case '\t':           /* \t  -->  \\t */
01790             *to++= '\\';
01791             *to++= 't';
01792             break;
01793           case '\\':           /* \  -->  \\ */
01794             *to++= '\\';
01795             *to++= '\\';
01796             break;
01797           case '\'':           /* '  -->  \' */
01798             *to++= '\\';
01799             *to++= '\'';
01800             break;
01801           case '"':            /* "  -->  \" */
01802             *to++= '\\';       /* Better safe than sorry */
01803             *to++= '"';
01804             break;
01805           case '\032':         /* Ctrl-Z (Win32 EOF)  -->  \\Z */
01806             if (data->shared->dbtype_id == DB_MYSQL)
01807             {
01808               *to++= '\\';       /* This gives problems on Win32 */
01809               *to++= 'Z';
01810             }
01811             else
01812             {
01813               *to++= *from; 
01814             }
01815             break;
01816           default:             /* copy character directly */
01817             *to++= *from; 
01818         }
01819       }
01820     }
01821     else
01822 #endif
01823     {
01824       for (end=from+from_length; from != end; from++)
01825       {
01826         switch(*from)
01827         {
01828           case '\'':           /*  '  -->  '' */      
01829             *to++= '\'';
01830             *to++= '\'';
01831             break;
01832           default:             /* copy character directly */
01833             *to++= *from;
01834         }
01835       }
01836     }
01837     *to=0;
01838     return(char *)to_start;
01839 }
01840 
01841 /*******************************************************************************
01842  * Function: UpdateLastCid(DatabaseData * data, int sid, int cid)
01843  *
01844  * Purpose: Sets the last cid used for a given a sensor ID (sid), 
01845  *
01846  * Arguments: data  : database information
01847  *            sid   : sensor ID
01848  *            cid   : event ID 
01849  *
01850  * Returns: status of the update
01851  *
01852  ******************************************************************************/
01853 int UpdateLastCid(DatabaseData *data, int sid, int cid)
01854 {
01855     char *insert0;
01856     int ret;
01857 
01858     insert0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01859     snprintf(insert0, MAX_QUERY_LENGTH,
01860              "UPDATE sensor "
01861              "   SET last_cid = %u "
01862              " WHERE sid = %u",
01863              cid, sid);
01864 
01865     ret = Insert(insert0, data);
01866     free(insert0);    insert0 = NULL;
01867     return ret;
01868 }
01869 
01870 /*******************************************************************************
01871  * Function: GetLastCid(DatabaseData * data, int sid)
01872  *
01873  * Purpose: Returns the last cid used for a given a sensor ID (sid), 
01874  *
01875  * Arguments: data  : database information
01876  *            sid   : sensor ID
01877  *
01878  * Returns: last cid for a given sensor ID (sid)
01879  *
01880  ******************************************************************************/
01881 int GetLastCid(DatabaseData *data, int sid)
01882 {
01883     char *select0;
01884     int tmp_cid;
01885 
01886     select0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01887     snprintf(select0, MAX_QUERY_LENGTH,
01888              "SELECT last_cid "
01889              "  FROM sensor "
01890              " WHERE sid = '%u'", sid);
01891 
01892     tmp_cid = Select(select0,data);
01893     free(select0);    select0 = NULL;
01894    
01895     return tmp_cid;
01896 }
01897 
01898 /*******************************************************************************
01899  * Function: CheckDBVersion(DatabaseData * data)
01900  *
01901  * Purpose: To determine the version number of the underlying DB schema
01902  *
01903  * Arguments: database information
01904  *
01905  * Returns: version number of the schema
01906  *
01907  ******************************************************************************/
01908 int CheckDBVersion(DatabaseData * data)
01909 {
01910    char *select0;
01911    int schema_version;
01912 
01913    select0 = (char *) SnortAlloc(MAX_QUERY_LENGTH+1);
01914 
01915 #if defined(ENABLE_MSSQL) || defined(ENABLE_ODBC)
01916    if ( data->shared->dbtype_id == DB_MSSQL ||
01917         (data->shared->dbtype_id==DB_ODBC && data->u_underlying_dbtype_id==DB_MSSQL) )
01918    {
01919       /* "schema" is a keyword in SQL Server, so use square brackets
01920        *  to indicate that we are referring to the table
01921        */
01922       snprintf(select0, MAX_QUERY_LENGTH,
01923                "SELECT vseq "
01924                "  FROM [schema]");
01925    }
01926    else
01927 #endif
01928    {
01929 #if defined(ENABLE_MYSQL)
01930       if (data->shared->dbtype_id == DB_MYSQL)
01931       {
01932          /* "schema" is a keyword in MYSQL, so use `schema`
01933           *  to indicate that we are referring to the table
01934           */
01935          snprintf(select0, MAX_QUERY_LENGTH,
01936                "SELECT vseq "
01937                "FROM `schema`");
01938       }
01939       else
01940 #endif
01941       {
01942          snprintf(select0, MAX_QUERY_LENGTH,
01943                "SELECT vseq "
01944                "FROM schema");
01945       }
01946    }
01947 
01948    schema_version = Select(select0,data);
01949    free(select0);    select0 = NULL;
01950 
01951    return schema_version;
01952 }
01953 
01954 /*******************************************************************************
01955  * Function: BeginTransaction(DatabaseData * data)
01956  *
01957  * Purpose: Database independent SQL to start a transaction
01958  * 
01959  ******************************************************************************/
01960 void BeginTransaction(DatabaseData * data)
01961 {
01962 #ifdef ENABLE_ODBC
01963     if ( data->shared->dbtype_id == DB_ODBC )
01964     {
01965         /* Do nothing.  ODBC will implicitly create a transaction. */
01966     }
01967     else
01968 #endif
01969 #ifdef ENABLE_MSSQL
01970     if ( data->shared->dbtype_id == DB_MSSQL )
01971     {
01972         Insert("BEGIN TRANSACTION", data);
01973     }
01974     else
01975 #endif
01976 #ifdef ENABLE_ORACLE
01977     if ( data->shared->dbtype_id == DB_ORACLE )
01978     {
01979         /* Do nothing.  Oracle will implicitly create a transaction. */
01980     }
01981     else
01982 #endif
01983     {
01984        Insert("BEGIN", data);
01985     }
01986 }
01987 
01988 /*******************************************************************************
01989  * Function: CommitTransaction(DatabaseData * data)
01990  *
01991  * Purpose: Database independent SQL to commit a transaction
01992  * 
01993  ******************************************************************************/
01994 void CommitTransaction(DatabaseData * data)
01995 {
01996 #ifdef ENABLE_ODBC
01997     if ( data->shared->dbtype_id == DB_ODBC )
01998     {
01999         if( SQLEndTran(SQL_HANDLE_DBC, data->u_connection, SQL_COMMIT) != SQL_SUCCESS )
02000         {
02001             ODBC_SQLRETURN  ret;
02002             ODBC_SQLCHAR    sqlState[6];
02003             ODBC_SQLCHAR    msg[SQL_MAX_MESSAGE_LENGTH];
02004             SQLINTEGER      nativeError;
02005             SQLSMALLINT     errorIndex = 1;
02006             SQLSMALLINT     msgLen;
02007 
02008             while ((ret = SQLGetDiagRec( SQL_HANDLE_DBC
02009                                        , data->u_connection
02010                                        , errorIndex
02011                                        , sqlState
02012                                        , &nativeError
02013                                        , msg
02014                                        , SQL_MAX_MESSAGE_LENGTH
02015                                        , &msgLen)) != SQL_NO_DATA)
02016             {
02017                 DEBUG_WRAP(LogMessage("database commit: %s\n", msg););
02018                 errorIndex++;
02019             }
02020         }
02021     }
02022     else
02023 #endif
02024 #ifdef ENABLE_MSSQL
02025     if ( data->shared->dbtype_id == DB_MSSQL )
02026     {
02027        Insert("COMMIT TRANSACTION", data);
02028     }
02029     else
02030 #endif
02031 #ifdef ENABLE_ORACLE
02032     if ( data->shared->dbtype_id == DB_ORACLE )
02033     {
02034        Insert("COMMIT WORK", data);
02035     }
02036     else
02037 #endif
02038     {
02039        Insert("COMMIT", data);
02040     }
02041 }
02042 
02043 /*******************************************************************************
02044  * Function: RollbackTransaction(DatabaseData * data)
02045  *
02046  * Purpose: Database independent SQL to rollback a transaction
02047  * 
02048  ******************************************************************************/
02049 void RollbackTransaction(DatabaseData * data)
02050 {
02051 #ifdef ENABLE_ODBC
02052     if ( data->shared->dbtype_id == DB_ODBC )
02053     {
02054         if( SQLEndTran(SQL_HANDLE_DBC, data->u_connection, SQL_ROLLBACK) != SQL_SUCCESS )
02055         {
02056             ODBC_SQLRETURN  ret;
02057             ODBC_SQLCHAR    sqlState[6];
02058             ODBC_SQLCHAR    msg[SQL_MAX_MESSAGE_LENGTH];
02059             SQLINTEGER      nativeError;
02060             SQLSMALLINT     errorIndex = 1;
02061             SQLSMALLINT     msgLen;
02062 
02063             while ((ret = SQLGetDiagRec( SQL_HANDLE_DBC
02064                                        , data->u_connection
02065                                        , errorIndex
02066                                        , sqlState
02067                                        , &nativeError
02068                                        , msg
02069                                        , SQL_MAX_MESSAGE_LENGTH
02070                                        , &msgLen)) != SQL_NO_DATA)
02071             {
02072                 DEBUG_WRAP(LogMessage("database rollback: %s\n", msg););
02073                 errorIndex++;
02074             }
02075         }
02076     }
02077     else
02078 #endif
02079 #ifdef ENABLE_MSSQL
02080     if ( data->shared->dbtype_id == DB_MSSQL )
02081     {
02082        Insert("ROLLBACK TRANSACTION", data);
02083     }
02084     else
02085 #endif
02086 #ifdef ENABLE_ORACLE
02087     if ( data->shared->dbtype_id == DB_ORACLE )
02088     {
02089        Insert("ROLLBACK WORK", data);
02090     }
02091     else
02092 #endif
02093     {
02094        Insert("ROLLBACK", data);
02095     }
02096 }
02097 
02098 /*******************************************************************************
02099  * Function: Insert(char * query, DatabaseData * data)
02100  *
02101  * Purpose: Database independent function for SQL inserts
02102  * 
02103  * Arguments: query (An SQL insert)
02104  *
02105  * Returns: 1 if successful, 0 if fail
02106  *
02107  ******************************************************************************/
02108 int Insert(char * query, DatabaseData * data)
02109 {
02110     int result = 0;
02111 
02112 #ifdef ENABLE_POSTGRESQL
02113     if( data->shared->dbtype_id == DB_POSTGRESQL )
02114     {
02115         data->p_result = PQexec(data->p_connection,query);
02116         if(!(PQresultStatus(data->p_result) != PGRES_COMMAND_OK))
02117         {
02118             result = 1;
02119         }
02120         else
02121         {
02122             if(PQerrorMessage(data->p_connection)[0] != '\0')
02123             {
02124                 ErrorMessage("database: postgresql_error: %s\n",
02125                              PQerrorMessage(data->p_connection));
02126             }
02127         } 
02128         PQclear(data->p_result);
02129     }
02130 #endif
02131 
02132 #ifdef ENABLE_MYSQL
02133     if(data->shared->dbtype_id == DB_MYSQL)
02134     {
02135         if(!(mysql_query(data->m_sock,query)))
02136         {
02137             result = 1;
02138         }
02139         else
02140         {
02141             if(mysql_errno(data->m_sock))
02142             {
02143               ErrorMessage("database: mysql_error: %s\nSQL=%s\n", 
02144                            mysql_error(data->m_sock), query);
02145             }
02146         }
02147     }
02148 #endif
02149 
02150 #ifdef ENABLE_ODBC
02151     if(data->shared->dbtype_id == DB_ODBC)
02152     {
02153         if(SQLAllocStmt(data->u_connection, &data->u_statement) == SQL_SUCCESS)
02154             if(SQLPrepare(data->u_statement, query, SQL_NTS) == SQL_SUCCESS)
02155                 if(SQLExecute(data->u_statement) == SQL_SUCCESS)
02156                 {
02157                     result = 1;
02158                 }
02159                 else
02160                 {
02161                     ODBC_SQLRETURN  ret;
02162                     ODBC_SQLCHAR    sqlState[6];
02163                     ODBC_SQLCHAR    msg[SQL_MAX_MESSAGE_LENGTH];
02164                     SQLINTEGER      nativeError;
02165                     SQLSMALLINT     errorIndex = 1;
02166                     SQLSMALLINT     msgLen;
02167 
02168                     /* assume no error unless nativeError tells us otherwise */
02169                     while ((ret = SQLGetDiagRec( SQL_HANDLE_STMT
02170                                                , data->u_statement
02171                                                , errorIndex
02172                                                , sqlState
02173                                                , &nativeError
02174                                                , msg
02175                                                , SQL_MAX_MESSAGE_LENGTH
02176                                                , &msgLen)) != SQL_NO_DATA)
02177                     {
02178                         DEBUG_WRAP(LogMessage("database: %s\n", msg););
02179                         errorIndex++;
02180                     }
02181                 }
02182     }
02183 #endif
02184 
02185 #ifdef ENABLE_ORACLE
02186     if(data->shared->dbtype_id == DB_ORACLE)
02187     {
02188         if(OCI_SUCCESS == OCIStmtPrepare(data->o_statement
02189                                        , data->o_error
02190                                        , query
02191                                        , strlen(query)
02192                                        , OCI_NTV_SYNTAX
02193                                        , OCI_DEFAULT))
02194         {
02195             if(OCI_SUCCESS == OCIStmtExecute(data->o_servicecontext
02196                                            , data->o_statement
02197                                            , data->o_error
02198                                            , 1
02199                                            , 0
02200                                            , NULL
02201                                            , NULL
02202                                            , OCI_COMMIT_ON_SUCCESS))
02203             {
02204                 result = 1;
02205             }
02206         }
02207 
02208         if( result != 1 )
02209         {
02210             OCIErrorGet(data->o_error
02211                       , 1
02212                       , NULL
02213                       , &data->o_errorcode
02214                       , data->o_errormsg
02215                       , sizeof(data->o_errormsg)
02216                       , OCI_HTYPE_ERROR);
02217             ErrorMessage("database: oracle_error: %s\n", data->o_errormsg);
02218             ErrorMessage("        : query: %s\n", query);
02219         } 
02220     }
02221 #endif
02222 
02223 #ifdef ENABLE_MSSQL
02224     if(data->shared->dbtype_id == DB_MSSQL)
02225     {
02226         SAVESTATEMENT(query);
02227         dbfreebuf(data->ms_dbproc);
02228         if( dbcmd(data->ms_dbproc, query) == SUCCEED )
02229             if( dbsqlexec(data->ms_dbproc) == SUCCEED )
02230                 if( dbresults(data->ms_dbproc) == SUCCEED )
02231                 {
02232                     while (dbnextrow(data->ms_dbproc) != NO_MORE_ROWS)
02233                     {
02234                         result = (int)data->ms_col;
02235                     }
02236                     result = 1;
02237                 }
02238         CLEARSTATEMENT();
02239     }
02240 #endif
02241 
02242 #ifdef DEBUG
02243     if(result)
02244     {
02245         DEBUG_WRAP(DebugMessage(DEBUG_LOG,"database(debug): (%s) executed\n", query););
02246     }
02247     else
02248     {
02249         DEBUG_WRAP(DebugMessage(DEBUG_LOG,"database(debug): (%s) failed\n", query););
02250     }
02251 #endif
02252 
02253     return result;
02254 }
02255 
02256 /*******************************************************************************
02257  * Function: Select(char * query, DatabaeData * data)
02258  *
02259  * Purpose: Database independent function for SQL selects that 
02260  *          return a non zero int
02261  * 
02262  * Arguments: query (An SQL insert)
02263  *
02264  * Returns: result of query if successful, 0 if fail
02265  *
02266  ******************************************************************************/
02267 int Select(char * query, DatabaseData * data)
02268 {
02269     int result = 0;
02270 
02271 #ifdef ENABLE_POSTGRESQL
02272     if( data->shared->dbtype_id == DB_POSTGRESQL )
02273     {
02274         data->p_result = PQexec(data->p_connection,query);
02275         if((PQresultStatus(data->p_result) == PGRES_TUPLES_OK))
02276         {
02277             if(PQntuples(data->p_result))
02278             {
02279                 if((PQntuples(data->p_result)) > 1)
02280                 {
02281                     ErrorMessage("database: warning (%s) returned more than one result\n",
02282                                  query);
02283                     result = 0;
02284                 }
02285                 else
02286                 {
02287                     result = atoi(PQgetvalue(data->p_result,0,0));
02288                 } 
02289             }
02290         }
02291         if(!result)
02292         {
02293             if(PQerrorMessage(data->p_connection)[0] != '\0')
02294             {
02295                 ErrorMessage("database: postgresql_error: %s\n",
02296                              PQerrorMessage(data->p_connection));
02297             }
02298         }
02299         PQclear(data->p_result);
02300     }
02301 #endif
02302 
02303 #ifdef ENABLE_MYSQL
02304     if(data->shared->dbtype_id == DB_MYSQL)
02305     {
02306         if(mysql_query(data->m_sock,query))
02307         {
02308             result = 0;
02309         }
02310         else
02311         {
02312             if(!(data->m_result = mysql_use_result(data->m_sock)))
02313             {
02314                 result = 0;
02315             }
02316             else
02317             {
02318                 if((data->m_row = mysql_fetch_row(data->m_result)))
02319                 {
02320                     if(data->m_row[0] != NULL)
02321                     {
02322                         result = atoi(data->m_row[0]);
02323                     }
02324                 }
02325             }
02326             mysql_free_result(data->m_result);
02327         }
02328         if(!result)
02329         {
02330             if(mysql_errno(data->m_sock))
02331             {
02332                 ErrorMessage("database: mysql_error: %s\n", mysql_error(data->m_sock));
02333             }
02334         }
02335     }
02336 #endif
02337 
02338 #ifdef ENABLE_ODBC
02339     if(data->shared->dbtype_id == DB_ODBC)
02340     {
02341         if(SQLAllocStmt(data->u_connection, &data->u_statement) == SQL_SUCCESS)
02342             if(SQLPrepare(data->u_statement, query, SQL_NTS) == SQL_SUCCESS)
02343                 if(SQLExecute(data->u_statement) == SQL_SUCCESS)
02344                     if(SQLRowCount(data->u_statement, &data->u_rows) == SQL_SUCCESS)
02345                         if(data->u_rows)
02346                         {
02347                             if(data->u_rows > 1)
02348                             {
02349                                 ErrorMessage("database: warning (%s) returned more than one result\n", query);
02350                                 result = 0;
02351                             }
02352                             else
02353                             {
02354                                 if(SQLFetch(data->u_statement) == SQL_SUCCESS)
02355                                     if(SQLGetData(data->u_statement,1,SQL_INTEGER,&data->u_col,
02356                                                   sizeof(data->u_col), NULL) == SQL_SUCCESS)
02357                                         result = (int)data->u_col;
02358                             }
02359                         }
02360     }
02361 #endif
02362 
02363 #ifdef ENABLE_ORACLE
02364     if(data->shared->dbtype_id == DB_ORACLE)
02365     {
02366         int success = 0;  /* assume it will fail */
02367         if(OCI_SUCCESS == OCIStmtPrepare(data->o_statement
02368                                        , data->o_error
02369                                        , query
02370                                        , strlen(query)
02371                                        , OCI_NTV_SYNTAX
02372                                        , OCI_DEFAULT))
02373         {
02374             if(OCI_SUCCESS == OCIDefineByPos(data->o_statement
02375                                            , &data->o_define
02376                                            , data->o_error
02377                                            , 1
02378                                            , &result
02379                                            , sizeof(result)
02380                                            , SQLT_INT
02381                                            , 0
02382                                            , 0
02383                                            , 0
02384                                            , OCI_DEFAULT))
02385             {
02386                 sword status;
02387                 status = OCIStmtExecute(data->o_servicecontext
02388                                                , data->o_statement
02389                                                , data->o_error
02390                                                , 1  /*0*/
02391                                                , 0
02392                                                , NULL
02393                                                , NULL
02394                                                , OCI_DEFAULT);
02395                 if( status==OCI_SUCCESS || status==OCI_NO_DATA )
02396                 {
02397                     success = 1;
02398                 }
02399             }
02400         }
02401         if( ! success )
02402         {
02403             OCIErrorGet(data->o_error
02404                       , 1
02405                       , NULL
02406                       , &data->o_errorcode
02407                       , data->o_errormsg
02408                       , sizeof(data->o_errormsg)
02409                       , OCI_HTYPE_ERROR);
02410             ErrorMessage("database: oracle_error: %s\n", data->o_errormsg);
02411             ErrorMessage("        : query: %s\n", query);
02412         }
02413     }
02414 #endif
02415 
02416 #ifdef ENABLE_MSSQL
02417     if(data->shared->dbtype_id == DB_MSSQL)
02418     {
02419         SAVESTATEMENT(query);
02420         dbfreebuf(data->ms_dbproc);
02421         if( dbcmd(data->ms_dbproc, query) == SUCCEED )
02422             if( dbsqlexec(data->ms_dbproc) == SUCCEED )
02423                 if( dbresults(data->ms_dbproc) == SUCCEED )
02424                     if( dbbind(data->ms_dbproc, 1, INTBIND, (DBINT) 0, (BYTE *) &data->ms_col) == SUCCEED )
02425                         while (dbnextrow(data->ms_dbproc) != NO_MORE_ROWS)
02426                         {
02427                             result = (int)data->ms_col;
02428                         }
02429         CLEARSTATEMENT();
02430     }
02431 #endif
02432 
02433 #ifdef DEBUG
02434     if(result)
02435     {
02436         DEBUG_WRAP(DebugMessage(DEBUG_LOG,"database(debug): (%s) returned %u\n", query, result););
02437     }
02438     else
02439     {
02440         DEBUG_WRAP(DebugMessage(DEBUG_LOG,"database(debug): (%s) failed\n", query););
02441     }
02442 #endif
02443 
02444     return result;
02445 }
02446 
02447 
02448 /*******************************************************************************
02449  * Function: Connect(DatabaseData * data)
02450  *
02451  * Purpose: Database independent function to initiate a database 
02452  *          connection
02453  *
02454  ******************************************************************************/
02455 void Connect(DatabaseData * data)
02456 {
02457 #ifdef ENABLE_MYSQL
02458     int x; 
02459 #endif
02460 
02461 #ifdef ENABLE_POSTGRESQL
02462     if( data->shared->dbtype_id == DB_POSTGRESQL )
02463     {
02464         data->p_connection = PQsetdbLogin(data->shared->host,data->port,NULL,NULL,data->shared->dbname,data->user,data->password);
02465         if(PQstatus(data->p_connection) == CONNECTION_BAD)
02466         {
02467             PQfinish(data->p_connection);
02468             FatalError("database: Connection to database '%s' failed\n", data->shared->dbname);
02469         }
02470     }
02471 #endif
02472 
02473 #ifdef ENABLE_MYSQL
02474     if(data->shared->dbtype_id == DB_MYSQL)
02475     {
02476         data->m_sock = mysql_init(NULL);
02477         if(data->m_sock == NULL)
02478         {
02479             FatalError("database: Connection to database '%s' failed\n", data->shared->dbname);
02480         }
02481         if(data->port != NULL)
02482         {
02483             x = atoi(data->port);
02484         }
02485         else
02486         {
02487             x = 0;
02488         }
02489         if(mysql_real_connect(data->m_sock, data->shared->host, data->user, data->password, data->shared->dbname, x, NULL, 0) == 0)
02490         {
02491             if(mysql_errno(data->m_sock))
02492             {
02493                 FatalError("database: mysql_error: %s\n", mysql_error(data->m_sock));
02494             }
02495             FatalError("database: Failed to logon to database '%s'\n", data->shared->dbname);
02496         }
02497     }
02498 #endif
02499 
02500 #ifdef ENABLE_ODBC
02501     if(data->shared->dbtype_id == DB_ODBC)
02502     {
02503         ODBC_SQLRETURN ret;
02504 
02505         data->u_underlying_dbtype_id = DB_UNDEFINED;
02506 
02507         if(!(SQLAllocEnv(&data->u_handle) == SQL_SUCCESS))
02508         {
02509             FatalError("database: unable to allocate ODBC environment\n");
02510         }
02511         if(!(SQLAllocConnect(data->u_handle, &data->u_connection) == SQL_SUCCESS))
02512         {
02513             FatalError("database: unable to allocate ODBC connection handle\n");
02514         }
02515 
02516         /* The SQL Server ODBC driver always returns SQL_SUCCESS_WITH_INFO
02517          * on a successful SQLConnect, SQLDriverConnect, or SQLBrowseConnect.
02518          * When an ODBC application calls SQLGetDiagRec after getting
02519          * SQL_SUCCESS_WITH_INFO, it can receive the following messages:
02520          * 5701 - Indicates that SQL Server put the user's context into the
02521          *        default database defined in the data source, or into the
02522          *        default database defined for the login ID used in the
02523          *        connection if the data source did not have a default database.
02524          * 5703 - Indicates the language being used on the server.
02525          * You can ignore messages 5701 and 5703; they are only informational.
02526          */
02527         ret = SQLConnect( data->u_connection
02528                         , data->shared->dbname
02529                         , SQL_NTS
02530                         , data->user
02531                         , SQL_NTS
02532                         , data->password
02533                         , SQL_NTS);
02534         if( ret != SQL_SUCCESS )
02535         {
02536             int  encounteredFailure = 1;  /* assume there is an error */
02537             char odbcError[2000];
02538             odbcError[0] = '\0';
02539 
02540             if( ret == SQL_SUCCESS_WITH_INFO )
02541             {
02542                 ODBC_SQLCHAR   sqlState[6];
02543                 ODBC_SQLCHAR   msg[SQL_MAX_MESSAGE_LENGTH];
02544                 SQLINTEGER     nativeError;
02545                 SQLSMALLINT    errorIndex = 1;
02546                 SQLSMALLINT    msgLen;
02547 
02548                 /* assume no error unless nativeError tells us otherwise */
02549                 encounteredFailure = 0;
02550 
02551                 while ((ret = SQLGetDiagRec( SQL_HANDLE_DBC
02552                                            , data->u_connection
02553                                            , errorIndex
02554                                            , sqlState
02555                                            , &nativeError
02556                                            , msg
02557                                            , SQL_MAX_MESSAGE_LENGTH
02558                                            , &msgLen)) != SQL_NO_DATA)
02559                 {
02560                     if( strstr(msg, "SQL Server") != NULL )
02561                     {
02562                         data->u_underlying_dbtype_id = DB_MSSQL;
02563                     }
02564 
02565                     if( nativeError!=5701 && nativeError!=5703 )
02566                     {
02567                         encounteredFailure = 1;
02568                         strncat(odbcError, msg, sizeof(odbcError));
02569                     }
02570                     errorIndex++;
02571                 }
02572             }
02573             if( encounteredFailure )
02574             {
02575                 FatalError("database: ODBC unable to connect.  %s\n", odbcError);
02576             }
02577         }
02578     }
02579 #endif
02580 
02581 #ifdef ENABLE_ORACLE
02582 
02583     #define PRINT_ORACLE_ERR(func_name) \
02584      { \
02585          OCIErrorGet(data->o_error, 1, NULL, &data->o_errorcode, \
02586                      data->o_errormsg, sizeof(data->o_errormsg), OCI_HTYPE_ERROR); \
02587          ErrorMessage("database: Oracle_error: %s\n", data->o_errormsg); \
02588          FatalError("database: %s : Connection to database '%s' failed\n", \
02589                     func_name, data->shared->dbname); \
02590      }
02591 
02592     if(data->shared->dbtype_id == DB_ORACLE)
02593     {
02594       if (!getenv("ORACLE_HOME")) 
02595       {
02596          ErrorMessage("database : ORACLE_HOME environment variable not set\n");
02597       }
02598  
02599       if (!data->user || !data->password || !data->shared->dbname) 
02600       { 
02601          ErrorMessage("database: user, password and dbname required for Oracle\n");
02602          ErrorMessage("database: dbname must also be in tnsnames.ora\n");
02603       }
02604 
02605       if (data->shared->host) 
02606       {
02607          ErrorMessage("database: hostname not required for Oracle, use dbname\n");
02608          ErrorMessage("database: dbname must be in tnsnames.ora\n");
02609       }
02610 
02611       if (OCIInitialize(OCI_DEFAULT, NULL, NULL, NULL, NULL)) 
02612          PRINT_ORACLE_ERR("OCIInitialize");
02613  
02614       if (OCIEnvInit(&data->o_environment, OCI_DEFAULT, 0, NULL)) 
02615          PRINT_ORACLE_ERR("OCIEnvInit");
02616  
02617       if (OCIEnvInit(&data->o_environment, OCI_DEFAULT, 0, NULL)) 
02618          PRINT_ORACLE_ERR("OCIEnvInit (2)");
02619  
02620       if (OCIHandleAlloc(data->o_environment, (dvoid **)&data->o_error, OCI_HTYPE_ERROR, (size_t) 0, NULL))
02621          PRINT_ORACLE_ERR("OCIHandleAlloc");
02622 
02623       if (OCILogon(data->o_environment, data->o_error, &data->o_servicecontext,
02624                    data->user, strlen(data->user), data->password, strlen(data->password), 
02625                    data->shared->dbname, strlen(data->shared->dbname))) 
02626       {   
02627          OCIErrorGet(data->o_error, 1, NULL, &data->o_errorcode, data->o_errormsg, sizeof(data->o_errormsg), OCI_HTYPE_ERROR);
02628          ErrorMessage("database: oracle_error: %s\n", data->o_errormsg);
02629          ErrorMessage("database: Checklist: check database is listed in tnsnames.ora\n");
02630          ErrorMessage("database:            check tnsnames.ora readable\n");
02631          ErrorMessage("database:            check database accessible with sqlplus\n");
02632          FatalError("database: OCILogon : Connection to database '%s' failed\n", data->shared->dbname);
02633       }
02634  
02635       if (OCIHandleAlloc(data->o_environment, (dvoid **)&data->o_statement, OCI_HTYPE_STMT, 0, NULL))
02636          PRINT_ORACLE_ERR("OCIHandleAlloc (2)");
02637     }
02638 #endif
02639 
02640 #ifdef ENABLE_MSSQL
02641     if(data->shared->dbtype_id == DB_MSSQL)
02642     {
02643         CLEARSTATEMENT();
02644         dberrhandle(mssql_err_handler);
02645         dbmsghandle(mssql_msg_handler);
02646 
02647         if( dbinit() != NULL )
02648         {
02649             data->ms_login = dblogin();
02650             if( data->ms_login == NULL )
02651             {
02652                 FatalError("database: Failed to allocate login structure\n");
02653             }
02654             /* Set up some informational values which are stored with the connection */
02655             DBSETLUSER (data->ms_login, data->user);
02656             DBSETLPWD  (data->ms_login, data->password);
02657             DBSETLAPP  (data->ms_login, "snort");
02658   
02659             data->ms_dbproc = dbopen(data->ms_login, data->shared->host);
02660             if( data->ms_dbproc == NULL )
02661             {
02662                 FatalError("database: Failed to logon to host '%s'\n", data->shared->host);
02663             }
02664             else
02665             {
02666                 if( dbuse( data->ms_dbproc, data->shared->dbname ) != SUCCEED )
02667                 {
02668                     FatalError("database: Unable to change context to database '%s'\n", data->shared->dbname);
02669                 }
02670             }
02671         }
02672         else
02673         {
02674             FatalError("database: Connection to database '%s' failed\n", data->shared->dbname);
02675         }
02676         CLEARSTATEMENT();
02677     }
02678 #endif
02679 }
02680 
02681 /*******************************************************************************
02682  * Function: Disconnect(DatabaseData * data)
02683  *
02684  * Purpose: Database independent function to close a connection
02685  *
02686  ******************************************************************************/
02687 void Disconnect(DatabaseData * data)
02688 {
02689     if( !pv.quiet_flag )
02690     {
02691       printf("database: Closing connection to database \"%s\"\n", 
02692              data->shared->dbname);
02693     }
02694 
02695     if(data)
02696     {
02697 #ifdef ENABLE_POSTGRESQL
02698         if(data->shared->dbtype_id == DB_POSTGRESQL)
02699         {
02700             if(data->p_connection)
02701             {
02702                 PQfinish(data->p_connection);
02703             }
02704         }
02705 #endif
02706 
02707 #ifdef ENABLE_MYSQL
02708         if(data->shared->dbtype_id == DB_MYSQL)
02709         {
02710             if(data->m_sock)
02711             {
02712                 mysql_close(data->m_sock);
02713             }
02714         }
02715 #endif
02716 
02717 #ifdef ENABLE_ODBC
02718         if(data->shared->dbtype_id == DB_ODBC)
02719         {
02720             if(data->u_handle)
02721             {
02722                 SQLDisconnect(data->u_connection); 
02723                 SQLFreeHandle(SQL_HANDLE_ENV, data->u_handle); 
02724             }
02725         }
02726 #endif
02727 
02728 #ifdef ENABLE_MSSQL
02729         if(data->shared->dbtype_id == DB_MSSQL)
02730         {
02731             CLEARSTATEMENT();
02732             if( data->ms_dbproc != NULL )
02733             {
02734                 dbfreelogin(data->ms_login);
02735                 data->ms_login = NULL;
02736                 dbclose(data->ms_dbproc);
02737                 data->ms_dbproc = NULL;
02738             }
02739         }
02740 #endif
02741     }
02742 }
02743 
02744 void DatabasePrintUsage()
02745 {
02746     puts("\nUSAGE: database plugin\n");
02747 
02748     puts(" output database: [log | alert], [type of database], [parameter list]\n");
02749     puts(" [log | alert] selects whether the plugin will use the alert or");
02750     puts(" log facility.\n");
02751 
02752     puts(" For the first argument, you must supply the type of database.");
02753     puts(" The possible values are mysql, postgresql, odbc, oracle and");
02754     puts(" mssql ");
02755 
02756     puts(" The parameter list consists of key value pairs. The proper");
02757     puts(" format is a list of key=value pairs each separated a space.\n");
02758 
02759     puts(" The only parameter that is absolutely necessary is \"dbname\"."); 
02760     puts(" All other parameters are optional but may be necessary");
02761     puts(" depending on how you have configured your RDBMS.\n");
02762 
02763     puts(" dbname - the name of the database you are connecting to\n"); 
02764 
02765     puts(" host - the host the RDBMS is on\n");
02766 
02767     puts(" port - the port number the RDBMS is listening on\n"); 
02768 
02769     puts(" user - connect to the database as this user\n");
02770 
02771     puts(" password - the password for given user\n");
02772 
02773     puts(" sensor_name - specify your own name for this snort sensor. If you");
02774     puts("        do not specify a name one will be generated automatically\n");
02775 
02776     puts(" encoding - specify a data encoding type (hex, base64, or ascii)\n");
02777 
02778     puts(" detail - specify a detail level (full or fast)\n");
02779 
02780     puts(" ignore_bpf - specify if you want to ignore the BPF part for a sensor\n");
02781     puts("              definition (yes or no, no is default)\n");
02782 
02783     puts(" FOR EXAMPLE:");
02784     puts(" The configuration I am currently using is MySQL with the database");
02785     puts(" name of \"snort\". The user \"snortusr@localhost\" has INSERT and SELECT");
02786     puts(" privileges on the \"snort\" database and does not require a password.");
02787     puts(" The following line enables snort to log to this database.\n");
02788 
02789     puts(" output database: log, mysql, dbname=snort user=snortusr host=localhost\n");
02790 }
02791 
02792 void SpoDatabaseCleanExitFunction(int signal, void *arg)
02793 {
02794     DatabaseData *data = (DatabaseData *)arg;
02795 
02796     DEBUG_WRAP(DebugMessage(DEBUG_LOG,"database(debug): entered SpoDatabaseCleanExitFunction\n"););
02797 
02798     UpdateLastCid(data, data->shared->sid, data->shared->cid-1);
02799     Disconnect(data); 
02800     if(data != NULL) 
02801     {
02802        free(data);
02803        data = NULL;
02804     }
02805 
02806     if(--instances == 0)
02807     {
02808        FreeSharedDataList();
02809     }
02810 }
02811 
02812 void SpoDatabaseRestartFunction(int signal, void *arg)
02813 {
02814     DatabaseData *data = (DatabaseData *)arg;
02815 
02816     DEBUG_WRAP(DebugMessage(DEBUG_LOG,"database(debug): entered SpoDatabaseRestartFunction\n"););
02817 
02818     UpdateLastCid(data, data->shared->sid, data->shared->cid-1);
02819     Disconnect(data);
02820     if(data != NULL) 
02821     {
02822        free(data);
02823        data = NULL;
02824     }
02825 
02826     if(--instances == 0)
02827     {
02828        FreeSharedDataList();
02829     }
02830 }
02831 
02832 void FreeSharedDataList()
02833 {
02834    SharedDatabaseDataNode *current;
02835 
02836    while(sharedDataList != NULL)
02837    { 
02838        current = sharedDataList;
02839        free(current->data);
02840        current->data = NULL;
02841        sharedDataList = current->next;
02842        free(current);
02843        current = NULL;
02844    }
02845 }
02846 
02847 #ifdef ENABLE_MSSQL
02848 /*
02849  * The functions mssql_err_handler() and mssql_msg_handler() are callbacks that are registered
02850  * when we connect to SQL Server.  They get called whenever SQL Server issues errors or messages.
02851  * This should only occur whenever an error has occurred, or when the connection switches to
02852  * a different database within the server.
02853  */
02854 static int mssql_err_handler(PDBPROCESS dbproc, int severity, int dberr, int oserr, 
02855                              LPCSTR dberrstr, LPCSTR oserrstr)
02856 {
02857     int retval;
02858     ErrorMessage("database: DB-Library error:\n\t%s\n", dberrstr);
02859 
02860     if ( severity == EXCOMM && (oserr != DBNOERR || oserrstr) )
02861         ErrorMessage("database: Net-Lib error %d:  %s\n", oserr, oserrstr);
02862     if ( oserr != DBNOERR )
02863         ErrorMessage("database: Operating-system error:\n\t%s\n", oserrstr);
02864 #ifdef ENABLE_MSSQL_DEBUG
02865     if( strlen(g_CurrentStatement) > 0 )
02866         ErrorMessage("database:  The above error was caused by the following statement:\n%s\n", g_CurrentStatement);
02867 #endif
02868     if ( (dbproc == NULL) || DBDEAD(dbproc) )
02869         retval = INT_EXIT;
02870     else
02871         retval = INT_CANCEL;
02872     return(retval);
02873 }
02874 
02875 
02876 static int mssql_msg_handler(PDBPROCESS dbproc, DBINT msgno, int msgstate, int severity, 
02877                              LPCSTR msgtext, LPCSTR srvname, LPCSTR procname, DBUSMALLINT line)
02878 {
02879     ErrorMessage("database: SQL Server message %ld, state %d, severity %d: \n\t%s\n",
02880                  msgno, msgstate, severity, msgtext);
02881     if ( (srvname!=NULL) && strlen(srvname)!=0 )
02882         ErrorMessage("Server '%s', ", srvname);
02883     if ( (procname!=NULL) && strlen(procname)!=0 )
02884         ErrorMessage("Procedure '%s', ", procname);
02885     if (line !=0) 
02886         ErrorMessage("Line %d", line);
02887     ErrorMessage("\n");
02888 #ifdef ENABLE_MSSQL_DEBUG
02889     if( strlen(g_CurrentStatement) > 0 )
02890         ErrorMessage("database:  The above error was caused by the following statement:\n%s\n", g_CurrentStatement);
02891 #endif
02892 
02893     return(0);
02894 }
02895 #endif

Generated on Sun May 14 14:51:14 2006 by  doxygen 1.4.2