/* SAMHAIN file system integrity testing */ /* Copyright (C) 2001 Rainer Wichmann */ /* */ /* This program is free software; you can redistribute it */ /* and/or modify */ /* it under the terms of the GNU General Public License as */ /* published by */ /* the Free Software Foundation; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config_xor.h" #include #include #include #include #include #include #include #ifdef WITH_DATABASE /* define this if you want to debug the Oracle database support */ /* #define DB_DEBUG */ #define SH_REAL_SET #include "samhain.h" #include "sh_cat.h" #include "sh_error.h" #include "sh_utils.h" extern int safe_logger (int signal, int method, pid_t thepid); #undef FIL__ #define FIL__ _("sh_database.c") typedef struct my_attr_ { char * attr; char * attr_o; int inHash; int val; int size; int alen; size_t off; } my_attr; typedef struct dbins_ { struct dbins_ * next; char host[64]; char time[20]; char msg[1024]; char sev[8]; char path[12288]; char user[9]; char group[9]; char program[8]; char subroutine[16]; char status[12]; char hash[50]; char path_data[1024]; char hash_data[50]; char key_uid[64]; char key_uid_data[64]; char key_id[16]; char module[8]; char syscall[16]; char ip[16]; char tty[16]; char peer[64]; char fromhost[64]; char obj[1024]; char interface[64]; char ltime[64]; char dir[1024]; char linked_path[1024]; char service[64]; char facility[32]; char priority[32]; char syslog_msg[1024]; char mode_old[16]; char mode_new[16]; char attr_old[16]; char attr_new[16]; char device_old[16]; char device_new[16]; char owner_old[9]; char owner_new[9]; char group_old[9]; char group_new[9]; char ctime_old[20]; char ctime_new[20]; char atime_old[20]; char atime_new[20]; char mtime_old[20]; char mtime_new[20]; char chksum_old[50]; char chksum_new[50]; char link_old[1024]; char link_new[1024]; long long_data[20]; /* long size_old; long size_new; long hardlinks_old; long hardlinks_new; long inode_old; long inode_new; */ } dbins; static my_attr * attr_tab_srch = NULL; static int attr_tab_srch_siz = 0; static my_attr attr_tab[] = { { NULL, N_("sev"), 0, 1, 8, 0, offsetof(struct dbins_, sev) }, { NULL, N_("tstamp"), 0, 2, 16, 0, offsetof(struct dbins_, time) }, { NULL, N_("remote_host"), 0, 3, 64, 0, offsetof(struct dbins_, host) }, { NULL, N_("msg"), 0, 4, 1024, 0, offsetof(struct dbins_, msg) }, { NULL, N_("path"), 0, 5,12288, 0, offsetof(struct dbins_, path) }, /* username -> userid; replace (long) 'userid' - below - by 'dummy' */ { NULL, N_("userid"), 0, 6, 9, 0, offsetof(struct dbins_, user) }, { NULL, N_("group"), 0, 7, 9, 0, offsetof(struct dbins_, group) }, { NULL, N_("program"), 0, 8, 8, 0, offsetof(struct dbins_, program) }, { NULL, N_("subroutine"), 0, 9, 16, 0, offsetof(struct dbins_, subroutine)}, { NULL, N_("status"), 0, 10, 12, 0, offsetof(struct dbins_, status) }, { NULL, N_("hash"), 0, 11, 50, 0, offsetof(struct dbins_, hash) }, { NULL, N_("path_data"), 0, 12, 1024, 0, offsetof(struct dbins_, path_data) }, { NULL, N_("hash_data"), 0, 13, 50, 0, offsetof(struct dbins_, hash_data) }, { NULL, N_("key_uid"), 0, 14, 64, 0, offsetof(struct dbins_, key_uid) }, { NULL, N_("key_uid_data"),0, 15, 64, 0, offsetof(struct dbins_, key_uid_data)}, { NULL, N_("key_id"), 0, 16, 16, 0, offsetof(struct dbins_, key_id) }, { NULL, N_("module"), 0, 17, 8, 0, offsetof(struct dbins_, module) }, { NULL, N_("syscall"), 0, 19, 16, 0, offsetof(struct dbins_, syscall) }, { NULL, N_("ip"), 0, 20, 16, 0, offsetof(struct dbins_, ip) }, { NULL, N_("tty"), 0, 21, 16, 0, offsetof(struct dbins_, tty) }, { NULL, N_("peer"), 0, 22, 64, 0, offsetof(struct dbins_, peer) }, { NULL, N_("obj"), 0, 23, 1024, 0, offsetof(struct dbins_, obj) }, { NULL, N_("interface"), 0, 24, 64, 0, offsetof(struct dbins_, interface)}, { NULL, N_("time"), 0, 25, 64, 0, offsetof(struct dbins_, ltime) }, { NULL, N_("dir"), 0, 26, 1024, 0, offsetof(struct dbins_, dir) }, { NULL, N_("linked_path"), 0, 27, 1024, 0, offsetof(struct dbins_, linked_path)}, { NULL, N_("service"), 0, 29, 64, 0, offsetof(struct dbins_, service)}, { NULL, N_("facility"), 0, 30, 32, 0, offsetof(struct dbins_, facility) }, { NULL, N_("priority"), 0, 31, 32, 0, offsetof(struct dbins_, priority) }, { NULL, N_("syslog_msg"), 0, 32, 1024, 0, offsetof(struct dbins_, syslog_msg) }, { NULL, N_("mode_old"), 0, 33, 16, 0, offsetof(struct dbins_, mode_old) }, { NULL, N_("mode_new"), 0, 34, 16, 0, offsetof(struct dbins_, mode_new) }, { NULL, N_("device_old"), 0, 35, 16, 0, offsetof(struct dbins_, device_old)}, { NULL, N_("device_new"), 0, 36, 16, 0, offsetof(struct dbins_, device_new)}, { NULL, N_("owner_old"), 0, 37, 9, 0, offsetof(struct dbins_, owner_old)}, { NULL, N_("owner_new"), 0, 38, 9, 0, offsetof(struct dbins_, owner_new)}, { NULL, N_("group_old"), 0, 39, 9, 0, offsetof(struct dbins_, group_old)}, { NULL, N_("group_new"), 0, 40, 9, 0, offsetof(struct dbins_, group_new)}, { NULL, N_("ctime_old"), 0, 41, 20, 0, offsetof(struct dbins_, ctime_old)}, { NULL, N_("ctime_new"), 0, 42, 20, 0, offsetof(struct dbins_, ctime_new)}, { NULL, N_("atime_old"), 0, 43, 20, 0, offsetof(struct dbins_, atime_old)}, { NULL, N_("atime_new"), 0, 44, 20, 0, offsetof(struct dbins_, atime_new)}, { NULL, N_("mtime_old"), 0, 45, 20, 0, offsetof(struct dbins_, mtime_old)}, { NULL, N_("mtime_new"), 0, 46, 20, 0, offsetof(struct dbins_, mtime_new)}, { NULL, N_("chksum_old"), 0, 47, 50, 0, offsetof(struct dbins_, chksum_old)}, { NULL, N_("chksum_new"), 0, 48, 50, 0, offsetof(struct dbins_, chksum_new)}, { NULL, N_("link_old"), 0, 49, 1024, 0, offsetof(struct dbins_, link_old)}, { NULL, N_("link_new"), 0, 50, 1024, 0, offsetof(struct dbins_, link_new)}, { NULL, N_("size_old"), 0, 51, 0, 0, 0 }, { NULL, N_("size_new"), 0, 52, 0, 0, 0 }, { NULL, N_("hardlinks_old"),0, 53, 0, 0, 0 }, { NULL, N_("hardlinks_new"),0, 54, 0, 0, 0 }, { NULL, N_("inode_old"), 0, 55, 0, 0, 0 }, { NULL, N_("inode_new"), 0, 56, 0, 0, 0 }, { NULL, N_("imode_old"), 0, 57, 0, 0, 0 }, { NULL, N_("imode_new"), 0, 58, 0, 0, 0 }, { NULL, N_("iattr_old"), 0, 59, 0, 0, 0 }, { NULL, N_("iattr_new"), 0, 60, 0, 0, 0 }, { NULL, N_("idevice_old"), 0, 61, 0, 0, 0 }, { NULL, N_("idevice_new"), 0, 62, 0, 0, 0 }, { NULL, N_("iowner_old"), 0, 63, 0, 0, 0 }, { NULL, N_("iowner_new"), 0, 64, 0, 0, 0 }, { NULL, N_("igroup_old"), 0, 65, 0, 0, 0 }, { NULL, N_("igroup_new"), 0, 66, 0, 0, 0 }, { NULL, N_("port"), 0, 67, 0, 0, 0 }, { NULL, N_("return_code"), 0, 68, 0, 0, 0 }, /* { NULL, N_("userid"), 0, 69, 0, 0 }, old 'userid', 1.8.1 */ { NULL, N_("host"), 0, 70, 64, 0, offsetof(struct dbins_, fromhost)}, { NULL, N_("attr_old"), 0, 71, 16, 0, offsetof(struct dbins_, attr_old)}, { NULL, N_("attr_new"), 0, 72, 16, 0, offsetof(struct dbins_, attr_new)}, { NULL, NULL, 0, 0, 0, 0, 0 } }; #define SH_SLOT_HOST 70 #define SH_SLOT_GROUP 7 #define START_SEC_LONGS 51 #define END_SEC_LONGS 68 #if defined(HAVE_INT_32) typedef unsigned int uint32; #elif defined(HAVE_LONG_32) typedef unsigned long uint32; #elif defined(HAVE_SHORT_32) typedef unsigned short uint32; #else #error No 32 byte type found ! #endif typedef unsigned char uint8; typedef struct { uint32 h[4]; uint32 data[16]; uint8 offset; uint32 nblocks; int count; } md5Param; typedef unsigned char sh_byte; extern int md5Reset(register md5Param* p); extern int md5Update(md5Param* p, const sh_byte* data, int size); extern int md5Digest(md5Param* p, uint32* data); static char db_name[64] = ""; static char db_table[64] = ""; static char db_host[64] = ""; static char db_user[64] = ""; static char db_password[64] = ""; static int sh_persistent_dbconn = S_TRUE; int sh_database_use_persistent (char * str) { return sh_util_flagval (str, &sh_persistent_dbconn); } static int insert_value (char * ptr, char * str) { if (!ptr || !str) return -1; if (strlen(str) > 63) return -1; (void) sl_strlcpy(ptr, str, 64); return 0; } static void init_db_entry (dbins * ptr) { memset (ptr, (int) '\0', sizeof(dbins)); ptr->next = NULL; return; } int sh_database_set_database (char * str) { return insert_value (db_name, str); } int sh_database_set_table (char * str) { return insert_value (db_table, str); } int sh_database_set_host (char * str) { return insert_value (db_host, str); } int sh_database_set_user (char * str) { return insert_value (db_user, str); } int sh_database_set_password (char * str) { return insert_value (db_password, str); } /****************************************************************** * * Oracle and unixODBC stuff, only Oracle tested untested * * Based on the code in the snort output plugin (spo_database.c). * Copyright/license statement in spo_database.c: * * Portions Copyright (C) 2000,2001,2002 Carnegie Mellon University * Copyright (C) 2001 Jed Pickel * Portions Copyright (C) 2001 Andrew R. Baker * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ******************************************************************/ #ifdef WITH_ODBC #include #include #include static SQLHENV u_handle; static SQLHDBC u_connection; static SQLHSTMT u_statement; static SQLINTEGER u_col; static SQLINTEGER u_rows; void sh_database_reset() { return; } static int sh_database_query (char * query, /*@out@*/ long * id) { static int fatal_error = 0; int result = 0; char row_query[128]; long result_call; SL_ENTER(_("sh_database_query")); *id = 0; if (fatal_error == 1) { SL_RETURN((-1), _("sh_database_query")); } /* Connect */ if (db_name[0] == '\0') sl_strlcpy(db_name, _("samhain"), 64); if (db_user[0] == '\0') sl_strlcpy(db_user, _("samhain"), 64); result_call = SQLAllocEnv(&u_handle); if ((result_call != SQL_SUCCESS) && (result_call != SQL_SUCCESS_WITH_INFO)) { sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, result_call, MSG_E_SUBGEN, _("Error in SQLAllocEnv when connecting to ODBC data source"), _("sh_database_query")); fatal_error = 1; SL_RETURN((-1), _("sh_database_query")); } result_call = SQLAllocConnect(u_handle, &u_connection); if ((result_call != SQL_SUCCESS) && (result_call != SQL_SUCCESS_WITH_INFO)) { sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, result_call, MSG_E_SUBGEN, _("Error in SQLAllocEnv when connecting to ODBC data source"), _("sh_database_query")); fatal_error = 1; SL_RETURN((-1), _("sh_database_query")); } result_call = SQLConnect(u_connection, db_name, SQL_NTS, db_user, SQL_NTS, db_password, SQL_NTS); if ((result_call != SQL_SUCCESS) && (result_call != SQL_SUCCESS_WITH_INFO)) { sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, result_call, MSG_E_SUBGEN, _("Error in SQLAllocEnv when connecting to ODBC data source"), _("sh_database_query")); fatal_error = 1; SL_RETURN((-1), _("sh_database_query")); } /* Insert */ result_call = SQLAllocStmt(u_connection, &u_statement); if ((result_call == SQL_SUCCESS) || (result_call == SQL_SUCCESS_WITH_INFO)) { result_call = SQLPrepare(u_statement, query, SQL_NTS); if ((result_call == SQL_SUCCESS) || (result_call == SQL_SUCCESS_WITH_INFO)) { result_call = SQLExecute(u_statement); if((result_call == SQL_SUCCESS) || (result_call == SQL_SUCCESS_WITH_INFO)) { result = 1; } } } if (result == 0) { sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, 0, MSG_E_SUBGEN, _("Error inserting into ODBC data source"), _("sh_database_query")); goto odbc_disconnect; } /* Select */ result = 0; sl_strlcpy (row_query, _("SELECT MAX(log_index) FROM "), 128); sl_strlcat (row_query, db_table, 128); result_call = SQLAllocStmt(u_connection, &u_statement); if ((result_call == SQL_SUCCESS) || (result_call == SQL_SUCCESS_WITH_INFO)) { result_call = SQLPrepare(u_statement, row_query, SQL_NTS); if ((result_call == SQL_SUCCESS) || (result_call == SQL_SUCCESS_WITH_INFO)) { result_call = SQLExecute(u_statement); if ((result_call == SQL_SUCCESS) || (result_call == SQL_SUCCESS_WITH_INFO)) { result_call = SQLRowCount(u_statement, &u_rows); if ((result_call == SQL_SUCCESS) || (result_call == SQL_SUCCESS_WITH_INFO)) { if((u_rows) && (u_rows == 1)) { result_call = SQLFetch(u_statement); if ((result_call == SQL_SUCCESS) || (result_call == SQL_SUCCESS_WITH_INFO)) { result_call = SQLGetData(u_statement, 1, SQL_INTEGER, &u_col, sizeof(u_col), NULL); if ((result_call == SQL_SUCCESS) || (result_call == SQL_SUCCESS_WITH_INFO)) { *id = (long int) u_col; result = 1; } } } } } } } if (result == 0) { sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, 0, MSG_E_SUBGEN, _("Error selecting MAX(log_index) from ODBC data source"), _("sh_database_query")); } odbc_disconnect: SQLFreeHandle(SQL_HANDLE_STMT, u_statement); SQLDisconnect(u_connection); SQLFreeHandle(SQL_HANDLE_DBC, u_connection); SQLFreeHandle(SQL_HANDLE_ENV, u_handle); SL_RETURN(((result == 0) ? -1 : 0), _("sh_database_query")); } /* #ifdef WITH_ODBC */ #endif #ifdef WITH_ORACLE #include static OCIDefine * o_define; static OCIEnv * o_environment; static OCISvcCtx * o_servicecontext; static OCIError * o_error = NULL; static OCIStmt * o_statement; static text o_errormsg[512]; static sb4 o_errorcode; static int connected = 0; void sh_database_reset() { if (connected == 1) { OCILogoff(o_servicecontext, o_error); OCIHandleFree((dvoid *) o_statement, OCI_HTYPE_STMT); OCIHandleFree((dvoid *) o_servicecontext, OCI_HTYPE_SVCCTX); OCIHandleFree((dvoid *) o_error, OCI_HTYPE_ERROR); o_error = NULL; } connected = 0; return; } static char * sh_stripnl (char * str) { size_t len = sl_strlen(str); if (len > 0) { if (str[len-1] == '\n') str[len-1] == '\0'; } return str; } static int sh_database_query (char * query, /*@out@*/ long * id) { static int bad_init = 0; int result = 0; char row_query[128]; int retry = 0; SL_ENTER(_("sh_database_query")); *id = 0; if (bad_init == 1) { SL_RETURN(-1, _("sh_database_query")); } else if (connected == 1) { goto oracle_connected; } /* * Connect */ #define PRINT_ORACLE_ERR(func_name) \ do { \ OCIErrorGet(o_error, 1, NULL, &o_errorcode, \ o_errormsg, sizeof(o_errormsg), \ OCI_HTYPE_ERROR); \ sh_stripnl (o_errormsg); \ sh_error_handle((-1), FIL__, __LINE__, (long) o_errorcode, MSG_E_SUBGEN, \ o_errormsg, _("sh_database_query")); \ sl_snprintf(row_query, 127, \ _("%s: Connection to database '%s' failed"), \ func_name, db_name); \ sh_error_handle((-1), FIL__, __LINE__, (long) o_errorcode, MSG_E_SUBGEN, \ row_query, _("sh_database_query")); \ bad_init = 1; \ SL_RETURN(-1, _("sh_database_query")); \ } while (1 == 0) oracle_doconnect: if (!getenv("ORACLE_HOME")) { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, _("ORACLE_HOME environment variable not set"), _("sh_database_query")); } if (db_name[0] == '\0') { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, _("database name not set, using default 'samhain'"), _("sh_database_query")); sl_strlcpy(db_name, _("samhain"), 64); } if (db_user[0] == '\0') { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, _("database user not set, using default 'samhain'"), _("sh_database_query")); sl_strlcpy(db_user, _("samhain"), 64); } if (db_password[0] == '\0') { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, _("database password not set, cannot proceed"), _("sh_database_query")); bad_init = 1; SL_RETURN(-1, _("sh_database_query")); } #ifdef DB_DEBUG sl_snprintf(row_query, 127, _("Conncting to oracle database '%s'"), db_name); sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, 0, MSG_E_SUBGEN, row_query, _("sh_database_query")); #endif /* a) Oracle says use OCIEnvCreate instead of OCIInitialize/OCIEnvcreate * b) why two times OCIEnvInit() ??? */ if (OCIInitialize(OCI_DEFAULT, NULL, NULL, NULL, NULL)) PRINT_ORACLE_ERR("OCIInitialize"); if (OCIEnvInit(&o_environment, OCI_DEFAULT, 0, NULL)) PRINT_ORACLE_ERR("OCIEnvInit"); if (OCIEnvInit(&o_environment, OCI_DEFAULT, 0, NULL)) PRINT_ORACLE_ERR("OCIEnvInit (2)"); /* allocate and initialize the error handle */ if (OCIHandleAlloc(o_environment, (dvoid **)&o_error, OCI_HTYPE_ERROR, (size_t) 0, NULL)) PRINT_ORACLE_ERR("OCIHandleAlloc"); /* logon and allocate the service context handle */ if (OCILogon(o_environment, o_error, &o_servicecontext, (OraText*) db_user, sl_strlen(db_user), (OraText*) db_password, sl_strlen(db_password), (OraText*) db_name, sl_strlen(db_name))) { OCIErrorGet(o_error, 1, NULL, &o_errorcode, o_errormsg, sizeof(o_errormsg), OCI_HTYPE_ERROR); sh_stripnl (o_errormsg); sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, o_errormsg, _("sh_database_query")); sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, _("check database is listed in tnsnames.ora"), _("sh_database_query")); sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, _("check tnsnames.ora readable"), _("sh_database_query")); sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, _("check database accessible with sqlplus"), _("sh_database_query")); sl_snprintf(row_query, 127, _("OCILogon: Connection to database '%s' failed"), db_name); sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, row_query, _("sh_database_query")); bad_init = 1; SL_RETURN(-1, _("sh_database_query")); } if (OCIHandleAlloc(o_environment, (dvoid **)&o_statement, OCI_HTYPE_STMT, 0, NULL)) PRINT_ORACLE_ERR("OCIHandleAlloc (2)"); /* Flag connection status */ connected = 1; oracle_connected: /* * Insert */ #ifdef DB_DEBUG sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, 0, MSG_E_SUBGEN, query, _("sh_database_query")); #endif if (OCIStmtPrepare(o_statement, o_error, (OraText*) query, sl_strlen(query), OCI_NTV_SYNTAX, OCI_DEFAULT)) { OCIErrorGet(o_error, 1, NULL, &o_errorcode, o_errormsg, sizeof(o_errormsg), OCI_HTYPE_ERROR); sh_stripnl (o_errormsg); sh_error_handle((-1), FIL__, __LINE__, (long) o_errorcode, MSG_E_SUBGEN, o_errormsg, _("sh_database_query")); if (retry == 0 && (3114 == o_errorcode || 0 == strncmp(o_errormsg, _("ORA-03114"), 9)) { ++retry; sh_database_reset(); goto oracle_doconnect; } goto err_out; } if (OCIStmtExecute(o_servicecontext, o_statement, o_error, 1, 0, NULL, NULL, OCI_COMMIT_ON_SUCCESS)) { OCIErrorGet(o_error, 1, NULL, &o_errorcode, o_errormsg, sizeof(o_errormsg), OCI_HTYPE_ERROR); sh_stripnl (o_errormsg); sh_error_handle((-1), FIL__, __LINE__, (long) o_errorcode, MSG_E_SUBGEN, o_errormsg, _("sh_database_query")); if (retry == 0 && (3114 == o_errorcode || 0 == strncmp(o_errormsg, _("ORA-03114"), 9)) { ++retry; sh_database_reset(); goto oracle_doconnect; } goto err_out; } #ifdef DB_DEBUG sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, 0, MSG_E_SUBGEN, _("No error on insert"), _("sh_database_query")); #endif /* Get row index */ sl_strlcpy (row_query, _("SELECT MAX(log_index) FROM "), 128); sl_strlcat (row_query, db_table, 128); #ifdef DB_DEBUG sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, 0, MSG_E_SUBGEN, row_query, _("sh_database_query")); #endif if (OCIStmtPrepare(o_statement, o_error, (OraText*) row_query, sl_strlen(row_query), OCI_NTV_SYNTAX, OCI_DEFAULT)) { OCIErrorGet(o_error, 1, NULL, &o_errorcode, o_errormsg, sizeof(o_errormsg), OCI_HTYPE_ERROR); sh_stripnl (o_errormsg); sh_error_handle((-1), FIL__, __LINE__, (long) o_errorcode, MSG_E_SUBGEN, o_errormsg, _("sh_database_query")); if (retry == 0 && (3114 == o_errorcode || 0 == strncmp(o_errormsg, _("ORA-03114"), 9)) { ++retry; sh_database_reset(); goto oracle_doconnect; } goto err_out; } if (OCIStmtExecute(o_servicecontext, o_statement, o_error, 0, 0, NULL, NULL, OCI_DEFAULT)) { OCIErrorGet(o_error, 1, NULL, &o_errorcode, o_errormsg, sizeof(o_errormsg), OCI_HTYPE_ERROR); sh_stripnl (o_errormsg); sh_error_handle((-1), FIL__, __LINE__, (long) o_errorcode, MSG_E_SUBGEN, o_errormsg, _("sh_database_query")); if (retry == 0 && (3114 == o_errorcode || 0 == strncmp(o_errormsg, _("ORA-03114"), 9)) { ++retry; sh_database_reset(); goto oracle_doconnect; } goto err_out; } if (OCIDefineByPos (o_statement, &o_define, o_error, 1, &result, sizeof(result), SQLT_INT, 0, 0, 0, OCI_DEFAULT)) { OCIErrorGet(o_error, 1, NULL, &o_errorcode, o_errormsg, sizeof(o_errormsg), OCI_HTYPE_ERROR); sh_stripnl (o_errormsg); sh_error_handle((-1), FIL__, __LINE__, (long) o_errorcode, MSG_E_SUBGEN, o_errormsg, _("sh_database_query")); if (retry == 0 && (3114 == o_errorcode || 0 == strncmp(o_errormsg, _("ORA-03114"), 9)) { ++retry; sh_database_reset(); goto oracle_doconnect; } goto err_out; } if (OCIStmtFetch (o_statement, o_error, 1, OCI_FETCH_NEXT, OCI_DEFAULT)) { OCIErrorGet(o_error, 1, NULL, &o_errorcode, o_errormsg, sizeof(o_errormsg), OCI_HTYPE_ERROR); sh_stripnl (o_errormsg); sh_error_handle((-1), FIL__, __LINE__, (long) o_errorcode, MSG_E_SUBGEN, o_errormsg, _("sh_database_query")); if (retry == 0 && (3114 == o_errorcode || 0 == strncmp(o_errormsg, _("ORA-03114"), 9)) { ++retry; sh_database_reset(); goto oracle_doconnect; } goto err_out; } #ifdef DB_DEBUG sl_snprintf(row_query, 127, _("Returned value: %d"), result); sh_error_handle(SH_ERR_NOTICE, FIL__, __LINE__, 0, MSG_E_SUBGEN, row_query, _("sh_database_query")); #endif *id = result; if (sh_persistent_dbconn == S_FALSE) { OCILogoff(o_servicecontext, o_error); OCIHandleFree((dvoid *) o_statement, OCI_HTYPE_STMT); OCIHandleFree((dvoid *) o_servicecontext, OCI_HTYPE_SVCCTX); OCIHandleFree((dvoid *) o_error, OCI_HTYPE_ERROR); o_error = NULL; connected = 0; } SL_RETURN(0, _("sh_database_query")); err_out: /* * Error */ if (sh_persistent_dbconn == S_FALSE) { OCILogoff(o_servicecontext, o_error); OCIHandleFree((dvoid *) o_statement, OCI_HTYPE_STMT); OCIHandleFree((dvoid *) o_servicecontext, OCI_HTYPE_SVCCTX); OCIHandleFree((dvoid *) o_error, OCI_HTYPE_ERROR); o_error = NULL; connected = 0; } SL_RETURN(-1, _("sh_database_query")); } /* #ifdef WITH_ORACLE */ #endif #ifdef WITH_POSTGRES /****************************************************************** * * Postgresql stuff, tested * ******************************************************************/ #ifdef HAVE_PGSQL_LIBPQ_FE_H #include #else #include #endif static int connection_status = S_FALSE; void sh_database_reset() { connection_status = S_FALSE; return; } static int sh_database_query (char * query, /*@out@*/ long * id) { char conninfo[256]; char * p; static PGconn * conn = NULL; PGresult * res; unsigned int i; static SH_TIMEOUT sh_timer = { 0, 3600, S_TRUE }; SL_ENTER(_("sh_database_query")); *id = 0; p = &conninfo[0]; if (db_host[0] == '\0') sl_strlcpy(db_host, _("localhost"), 64); if (db_name[0] == '\0') sl_strlcpy(db_name, _("samhain"), 64); if (db_user[0] == '\0') sl_strlcpy(db_user, _("samhain"), 64); if (db_host[0] != '\0' && NULL != strchr(db_host, '.')) { sl_snprintf(p, 255, "hostaddr=%s ", db_host); p = &conninfo[strlen(conninfo)]; } if (db_name[0] != '\0') { sl_snprintf(p, 255 - strlen(conninfo), "dbname=%s ", db_name); p = &conninfo[strlen(conninfo)]; } if (db_user[0] != '\0') { sl_snprintf(p, 255 - strlen(conninfo), "user=%s ", db_user); p = &conninfo[strlen(conninfo)]; } if (db_password[0] != '\0') { sl_snprintf(p, 255 - strlen(conninfo), "password=%s ", db_password); } if (connection_status == S_FALSE) { if (conn) PQfinish(conn); conn = NULL; conn = PQconnectdb(conninfo); } else { if (PQstatus(conn) == CONNECTION_BAD) PQreset(conn); } if ((conn == NULL) || (PQstatus(conn) == CONNECTION_BAD)) { connection_status = S_FALSE; sh_timer.flag_ok = S_FALSE; if (S_TRUE == sh_util_timeout_check(&sh_timer)) { goto err_out; } else { if (conn) PQfinish(conn); conn = NULL; SL_RETURN(0, _("sh_database_query")); } } connection_status = S_TRUE; /* do the insert */ res = PQexec(conn, query); if (PQresultStatus(res) != PGRES_COMMAND_OK) { PQclear(res); goto err_out; } PQclear(res); /* get the unique row index */ res = PQexec(conn, _("SELECT last_value FROM log_log_index_seq")); if (PQresultStatus(res) != PGRES_TUPLES_OK) { PQclear(res); goto err_out; } *id = atoi (PQgetvalue(res, 0, 0)); PQclear(res); if (S_FALSE == sh_persistent_dbconn) { if (conn) PQfinish(conn); conn = NULL; connection_status = S_FALSE; } SL_RETURN(0, _("sh_database_query")); err_out: if (conn) { p = PQerrorMessage(conn); for (i = 0; i < sl_strlen(p); ++i) if (p[i] == '\n') p[i] = ' '; } else { p = NULL; } sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, (p == NULL ? _("(null)") : p), _("sh_database_query")); if (conn) PQfinish(conn); conn = NULL; connection_status = S_FALSE; SL_RETURN(-1, _("sh_database_query")); } #endif #ifdef WITH_MYSQL #ifdef HAVE_MYSQL_MYSQL_H #include #else #include #endif extern int flag_err_debug; static int connection_status = S_FALSE; void sh_database_reset() { connection_status = S_FALSE; return; } static int sh_database_query (char * query, /*@out@*/ long * id) { int status = 0; const char * p; static MYSQL * db_conn = NULL; static SH_TIMEOUT sh_timer = { 0, 3600, S_TRUE }; SL_ENTER(_("sh_database_query")); *id = 0; if (query == NULL) { sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN, _("NULL query"), _("sh_database_query")); SL_RETURN(0, _("sh_database_query")); } if (db_host[0] == '\0') (void) sl_strlcpy(db_host, _("localhost"), 64); if (db_name[0] == '\0') (void) sl_strlcpy(db_name, _("samhain"), 64); if (db_user[0] == '\0') (void) sl_strlcpy(db_user, _("samhain"), 64); if ((db_conn == NULL) || (connection_status == S_FALSE)) { if (db_conn) { mysql_close(db_conn); db_conn = NULL; } connection_status = S_FALSE; db_conn = mysql_init(NULL); if (NULL == db_conn) { p = NULL; status = 0; sh_timer.flag_ok = S_FALSE; if (S_TRUE == sh_util_timeout_check(&sh_timer)) { goto alt_out; } else { SL_RETURN(0, _("sh_database_query")); } } /* Read in defaults from /etc/my.cnf and associated files, * suggested by arjones at simultan dyndns org * see: - http://dev.mysql.com/doc/refman/5.0/en/option-files.html * for the my.cnf format, * - http://dev.mysql.com/doc/refman/5.0/en/mysql-options.html * for possible options * We don't check the return value because it's useless (failure due * to lack of access permission is not reported). */ mysql_options(db_conn, MYSQL_READ_DEFAULT_GROUP, _("samhain")); status = 0; if (NULL == mysql_real_connect(db_conn, db_host[0] == '\0' ? NULL : db_host, db_user[0] == '\0' ? NULL : db_user, db_password[0] == '\0' ? NULL : db_password, db_name[0] == '\0' ? NULL : db_name, 0, NULL, 0)) { sh_timer.flag_ok = S_FALSE; if (S_TRUE == sh_util_timeout_check(&sh_timer)) { goto err_out; } else { SL_RETURN(0, _("sh_database_query")); } } connection_status = S_TRUE; } else { if (0 != mysql_ping(db_conn)) { connection_status = S_FALSE; sh_timer.flag_ok = S_FALSE; if (S_TRUE == sh_util_timeout_check(&sh_timer)) { goto err_out; } else { SL_RETURN(0, _("sh_database_query")); } } } if (0 != mysql_query(db_conn, query)) { goto err_out; } if (flag_err_debug == SL_TRUE) { p = mysql_info (db_conn); if (p != NULL) { sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN, p, _("sh_database_query")); } } *id = (long) mysql_insert_id(db_conn); if (S_FALSE == sh_persistent_dbconn) { if (db_conn) mysql_close(db_conn); db_conn = NULL; connection_status = S_FALSE; } SL_RETURN(0, _("sh_database_query")); err_out: if (db_conn) { p = mysql_error (db_conn); status = (int) mysql_errno (db_conn); } else { p = NULL; p = 0; } alt_out: *id = 0; sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_SUBGEN, (p == NULL ? _("(null)") : p), _("sh_database_query")); if (db_conn) mysql_close(db_conn); db_conn = NULL; connection_status = S_FALSE; SL_RETURN(status, _("sh_database_query")); } #endif static char * null_or_val (char * end, char * val, int * size, int flag) { long len; if (!end || !val || !size) return end; if (val[0] == '\0') { return end; } else { if (*size > 1) { *end = ','; ++end; (*size) -= 1; if (flag == 1) { *end = '\''; ++end; (*size) -= 1; } *end = '\0'; } len = (long) strlen(val); if ((long) *size > (len+1)) { (void) sl_strlcat(end, val, (size_t) *size); end += len; (*size) -= len; if (flag == 1) { *end = '\''; ++end; (*size) -= 1; } *end = '\0'; } } #if 0 /* apparently slower, see gyule.7 */ len = (long) strlen(val); if ((val[0] != '\0') && (*size > 2)) { if (flag == 1) { *end = ','; ++end; *end = '\''; ++end; (*size) -= 2; *end = '\0'; if ((long) *size > (len+2)) { (void) sl_strlcat(end, val, (size_t) *size); end += len; (*size) -= len; *end = '\''; ++end; (*size) -= 1; } *end = '\0'; } else { *end = ','; ++end; (*size) -= 1; *end = '\0'; if ((long) *size > (len+1)) { (void) sl_strlcat(end, val, (size_t) *size); end += len; (*size) -= len; } *end = '\0'; } } #endif return end; } #define SH_QUERY_MAX 16383 static long sh_database_entry (dbins * db_entry, long id) { /* This does not need to be re-entrant */ char * query; static char columns[1024]; char * values; int status; long the_id; int size; char * end; int c_size; char * c_end; char * p; int i; char num[64]; md5Param crc; unsigned char md5buffer[16]; char md5out[33]; int cnt; SL_ENTER(_("sh_database_entry")); query = SH_ALLOC(SH_QUERY_MAX+1); values = SH_ALLOC(SH_QUERY_MAX+1); (void) md5Reset(&crc); if (db_entry->host[0] == '\0') { if (sh.host.name[0] == '\0') (void) strcpy (db_entry->host, _("localhost")); /* known to fit */ else (void) sl_strlcpy (db_entry->host, sh.host.name, 64); } /*@-bufferoverflowhigh@*/ if (id >= 0) sprintf(num, "%ld", id); /* known to fit */ /*@+bufferoverflowhigh@*/ #ifdef WITH_ORACLE /* Oracle needs some help for the time format (fix by Michael Somers) */ (void) sl_snprintf (values, SH_QUERY_MAX, _("(%s,%c%s%c,to_date(%c%s%c,'YYYY-MM-DD HH24:MI:SS'),%c%s%c,%c%s%c"), id >= 0 ? num : _("NULL"), '\'', db_entry->host,'\'', '\'', db_entry->time,'\'', '\'', db_entry->sev, '\'', '\'', (db_entry->msg[0] == '\0' ? _("NULL") : db_entry->msg), '\''); #else (void) sl_snprintf (values, SH_QUERY_MAX, _("(%s,%c%s%c,%c%s%c,%c%s%c,%c%s%c"), id >= 0 ? num : _("NULL"), '\'', db_entry->host,'\'', '\'', db_entry->time,'\'', '\'', db_entry->sev, '\'', '\'', (db_entry->msg[0] == '\0' ? _("NULL") : db_entry->msg), '\''); #endif (void) sl_snprintf (columns, 1023, _("(log_ref,log_host,log_time,log_sev,log_msg")); /*@-type@*//* byte* versus char[..] */ if (attr_tab[0].inHash == 1) (void) md5Update(&crc, (sh_byte*) db_entry->sev, (int) strlen(db_entry->sev)); if (attr_tab[1].inHash == 1) (void) md5Update(&crc, (sh_byte*) db_entry->time, (int) strlen(db_entry->time)); if (attr_tab[2].inHash == 1) (void) md5Update(&crc, (sh_byte*) db_entry->host, (int) strlen(db_entry->host)); if (attr_tab[3].inHash == 1 && db_entry->msg[0] != '\0') (void) md5Update(&crc, (sh_byte*) db_entry->msg, (int) strlen(db_entry->sev)); /*@+type@*/ size = (int) (SH_QUERY_MAX - strlen(values)); end = values + strlen(values); c_size = 1023 - (int) strlen(columns); /* sizeof(colums) == 1024 */ c_end = columns + strlen(columns); i = 4; while (attr_tab[i].attr != NULL) { if (attr_tab[i].size != 0) { if (attr_tab[i].val > 40 && attr_tab[i].val < 47) { /* remove the 'T' between date and time */ p = (char *)(db_entry)+attr_tab[i].off; p = strchr(p, 'T'); if (p) *p = ' '; } p = end; end = null_or_val(end,((char *)(db_entry)+attr_tab[i].off),&size,1); if (p != end) { /* * 'host' is a reserved word in SQL */ if (attr_tab[i].val == SH_SLOT_HOST) c_end = null_or_val (c_end, _("fromhost"), &c_size,0); /* * 'group' is a reserved word in SQL */ else if (attr_tab[i].val == SH_SLOT_GROUP) c_end = null_or_val (c_end, _("grp"), &c_size,0); else c_end = null_or_val (c_end, attr_tab[i].attr, &c_size,0); } /*@-type@*//* byte* versus char[..] */ if (attr_tab[i].inHash == 1 && ((char *)(db_entry)+attr_tab[i].off) != '\0') { (void)md5Update(&crc, (sh_byte*) ((char *)(db_entry)+attr_tab[i].off), (int)strlen((char *)(db_entry)+attr_tab[i].off)); } /*@+type@*/ } else if (attr_tab[i].val >= START_SEC_LONGS && attr_tab[i].val <= END_SEC_LONGS) { (void) sl_snprintf(end, (size_t)(size-1), _(",\'%ld\'"), db_entry->long_data[attr_tab[i].val-START_SEC_LONGS]); while (*end != '\0') { ++end; --size; } (void) sl_snprintf(c_end, (size_t)(c_size-1), _(",%s"), attr_tab[i].attr); while (*c_end != '\0') { ++c_end; --c_size; } if (attr_tab[i].inHash == 1) { /*@-type@*//* byte* versus char[..] */ (void) md5Update(&crc, (sh_byte *) db_entry->long_data[attr_tab[i].val-START_SEC_LONGS], sizeof(long)); /*@+type@*/ } } ++i; } (void) md5Digest(&crc, (uint32 *) md5buffer); /*@-bufferoverflowhigh -usedef@*/ for (cnt = 0; cnt < 16; ++cnt) sprintf (&md5out[cnt*2], _("%02X"), /* known to fit */ (unsigned int) md5buffer[cnt]); /*@+bufferoverflowhigh +usedef@*/ md5out[32] = '\0'; (void) sl_snprintf(end, (size_t) (size-1), _(",%c%s%c"), '\'', md5out, '\''); while (*end != '\0') { ++end; --size; } (void) sl_snprintf(c_end, (size_t) (c_size-1),_(",log_hash")); while (*c_end != '\0') { ++c_end; --c_size; } if (size > 1) { *end = ')'; ++end; *end = '\0'; } if (c_size > 1) { *c_end = ')'; ++c_end; *c_end = '\0'; } if (db_table[0] == '\0') (void) sl_strlcpy(db_table, _("log"), 64); (void) sl_snprintf (query, SH_QUERY_MAX, _("INSERT INTO %s %s VALUES %s"), db_table, columns, values); status = sh_database_query (query, &the_id); /*@-usedef@*//* no, 'values' is allocated here */ SH_FREE(values); /*@+usedef@*/ SH_FREE(query); SL_RETURN(the_id, _("sh_database_entry")); } static int sh_database_comp_attr (const void *m1, const void *m2) { my_attr *mi1 = (my_attr *) m1; my_attr *mi2 = (my_attr *) m2; return strcmp(mi1->attr, mi2->attr); } static void init_attr_table() { static int first = S_TRUE; int i, j; #ifdef SH_STEALTH int j, k; if (first == S_FALSE) return; i = 0; while (attr_tab[i].attr_o != NULL) { j = strlen(attr_tab[i].attr_o); attr_tab[i].attr = malloc (j+1); /* only once */ if (NULL == attr_tab[i].attr) return; for (k = 0; k < j; ++k) attr_tab[i].attr[k] = attr_tab[i].attr_o[k] ^ XOR_CODE; attr_tab[i].attr[j] = '\0'; attr_tab[i].alen = strlen(attr_tab[i].attr_o); ++i; } first = S_FALSE; #else i = 0; while (attr_tab[i].attr_o != NULL) { attr_tab[i].attr = attr_tab[i].attr_o; attr_tab[i].alen = strlen(attr_tab[i].attr_o); ++i; } first = S_FALSE; #endif /* create a sorted table for binary search */ attr_tab_srch = SH_ALLOC(i * sizeof(my_attr)); for (j=0; j')) { p = &p[4]; goto parse; } else if (p[0] == '/' && p[1] == '>') SL_RETURN (&p[2], _("sh_database_parse")); else if (p[0] == '/' && p[1] == 'l' && p[2] == 'o' && p[3] == 'g' && p[4] == '>') SL_RETURN (&p[5], _("sh_database_parse")); ++p; } SL_RETURN(NULL, _("sh_database_parse")); parse: while (*p == ' ' || *p == '>') ++p; if (*p == '\0') SL_RETURN(NULL, _("sh_database_parse")); if (*p != '<' && *p != '/') goto par2; if (p[0] == '<' && p[1] == 'l' && p[2] == 'o' && p[3] == 'g') { new = SH_ALLOC(sizeof(dbins)); init_db_entry(new); db_entry->next = new; p = sh_database_parse (p, new); } if (p[0] == '/' && p[1] == '>') SL_RETURN (&p[1], _("sh_database_parse")); if (p[0] == '<' && p[1] == '/' && p[2] == 'l' && p[3] == 'o' && p[4] == 'g' && p[5] == '>') SL_RETURN (&p[5], _("sh_database_parse")); par2: /* non-whitespace */ i = 0; for (i=0; i < 64; ++i) { key_str[i] = p[i]; if (p[i] == '=') { key_str[i] = '\0'; break; } } key_str[63] = '\0'; key.attr = &key_str[0]; res = bsearch(&key, attr_tab_srch, attr_tab_srch_siz, sizeof(my_attr), sh_database_comp_attr); if (res != NULL) { j = res->alen; /* strlen(attr_tab[i].attr); */ if (p[j] == '=' && p[j+1] == '"') { q = strchr(&p[j+2], '"'); if (!q) { SL_RETURN(NULL, _("sh_database_parse")); } else { *q = '\0'; if (S_FALSE == is_escaped(&p[j+2])) { sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, _("Message not properly escaped"), _("sh_database_parse")); SL_RETURN(NULL, _("sh_database_parse")); } if (res->val == 1) (void) sl_strlcpy(db_entry->sev, &p[j+2], (size_t)res->size); else if (res->val == 2) { z = strchr(&p[j+2], 'T'); if (z) *z = ' '; (void) sl_strlcpy(db_entry->time, &p[j+2], 20); } else if (res->val == 3) (void) sl_strlcpy(db_entry->host, &p[j+2], (size_t) res->size); else if (res->val == 4) (void) sl_strlcpy(db_entry->msg, &p[j+2], (size_t) res->size); else if (res->size != 0) { (void) sl_strlcpy( (((char *)(db_entry))+ res->off), &p[j+2], (size_t) res->size); } else if (res->val >= START_SEC_LONGS) { db_entry->long_data[res->val-START_SEC_LONGS] = atol(&p[j+2]); } *q = '"'; p = q; ++p; goto parse; } } } /* unknown attribute, skip */ while ((p != NULL) && (*p != '\0') && (*p != ' ')) ++p; goto parse; } static int enter_wrapper = 1; int set_enter_wrapper (char * str) { return sh_util_flagval(str, &enter_wrapper); } int sh_database_insert (char * message) { dbins * db_entry; dbins * prev; dbins * curr; long id = 0; #ifdef HOST_SWITCH char * temp[64]; #endif SL_ENTER(_("sh_database_insert")); db_entry = SH_ALLOC(sizeof(dbins)); init_db_entry(db_entry); /* recursively parse the message into a linked list */ (void) sh_database_parse (message, db_entry); /* Enter the list into the database. Actually, the list can only have * two entries at most. */ curr = db_entry; if (curr->next) { prev = curr->next; #ifdef HOST_SWITCH strncpy(temp, prev->host, 64); #endif strncpy(prev->host, curr->host, 64); #ifdef HOST_SWITCH strncpy(curr->host, temp, 64); #endif id = sh_database_entry (prev, -1); SH_FREE(prev); } if (id != 0) /* this is a server wrapper */ { if (enter_wrapper != 0) (void) sh_database_entry (curr, id); } else /* this is a generic server message */ { (void) sh_database_entry (curr, 0); } SH_FREE(curr); SL_RETURN(0, _("sh_database_insert")); } #endif