int parse_proto_program( char *program ) { int i; for (i = 0; i < counters->mapcount_program; i++) { if ( map_program[i].nocase == 1 ) { if (Sagan_stristr(program, map_program[i].program, TRUE)) return(map_program[i].proto); } else { if (Sagan_strstr(program, map_program[i].program)) return(map_program[i].proto); } } return(0); }
int parse_proto( char *msg ) { int i; for (i = 0; i < counters->mapcount_message; i++) { if ( map_message[i].nocase == 1 ) { if (Sagan_stristr(msg, map_message[i].search, TRUE)) return(map_message[i].proto); } else { if (Sagan_strstr(msg, map_message[i].search)) return(map_message[i].proto); } } return(0); }
char *Sagan_Parse_IP( char *syslogmessage, int pos ) { int result_space, result_nonspace, i, b; int flag=0; int current_pos=0; int notfound=0; char ctmp[2] = { 0 }; char lastgood[16] = { 0 }; char msg[MAX_SYSLOGMSG] = { 0 }; char tmpmsg[MAX_SYSLOGMSG] = { 0 }; char *retbuf = NULL; char *tok=NULL; char *ptmp=NULL; struct sockaddr_in sa; snprintf(tmpmsg, sizeof(tmpmsg), "%s", syslogmessage); ptmp = strtok_r(tmpmsg, " ", &tok); while (ptmp != NULL ) { if (Sagan_strstr(ptmp, ".")) { result_space = inet_pton(AF_INET, ptmp, &(sa.sin_addr)); /* If we already have a good IP, return it. We can sometimes skips * the next steps */ if ( result_space != 0 && strcmp(ptmp, "127.0.0.1")) { current_pos++; if ( current_pos == pos ) { return(ptmp); } } else { notfound = 1; } /* Start tearing apart the substring */ if ( notfound == 1 ) { for (b=0; b < strlen(ptmp); b++) { for (i = b; i < strlen(ptmp); i++) { snprintf(ctmp, sizeof(ctmp), "%c", ptmp[i]); strlcat(msg, ctmp, sizeof(msg)); result_nonspace = inet_pton(AF_INET, msg, &(sa.sin_addr)); if ( result_nonspace != 0 ) { strlcpy(lastgood, msg, sizeof(lastgood)); flag=1; } if ( flag == 1 && result_nonspace == 0 ) { current_pos++; if ( current_pos == pos ) { if (!strcmp(lastgood, "127.0.0.1")) return(config->sagan_host); retbuf=lastgood; return(retbuf); } flag = 0; i=i+strlen(lastgood); b=b+strlen(lastgood); break; } } strlcpy(msg, "", sizeof(msg)); } } notfound = 0; } ptmp = strtok_r(NULL, " ", &tok); } return("0"); }
int Sagan_Flowbit_Condition(int rule_position, char *ip_src_char, char *ip_dst_char ) { time_t t; struct tm *now; char timet[20]; char tmp[128] = { 0 }; char *tmp_flowbit_name = NULL; char *tok = NULL; int i; int a; uint32_t ip_src; uint32_t ip_dst; sbool flowbit_match = 0; int flowbit_total_match = 0; t = time(NULL); now=localtime(&t); strftime(timet, sizeof(timet), "%s", now); ip_src = IP2Bit(ip_src_char); ip_dst = IP2Bit(ip_dst_char); int and_or = 0; Sagan_Flowbit_Cleanup(); for (i = 0; i < rulestruct[rule_position].flowbit_count; i++) { /******************* * ISSET * *******************/ if ( rulestruct[rule_position].flowbit_type[i] == 3 ) { for (a = 0; a < counters_ipc->flowbit_count; a++) { strlcpy(tmp, rulestruct[rule_position].flowbit_name[i], sizeof(tmp)); if (Sagan_strstr(rulestruct[rule_position].flowbit_name[i], "|")) { tmp_flowbit_name = strtok_r(tmp, "|", &tok); and_or = 1; } else { tmp_flowbit_name = strtok_r(tmp, "&", &tok); and_or = 0; /* Need this? */ } while (tmp_flowbit_name != NULL ) { if (!strcmp(tmp_flowbit_name, flowbit_ipc[a].flowbit_name) && flowbit_ipc[a].flowbit_state == 1 ) { /* direction: none */ if ( rulestruct[rule_position].flowbit_direction[i] == 0 ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] \"isset\" flowbit \"%s\" (direction: \"none\"). (any -> any)", __FILE__, __LINE__, flowbit_ipc[a].flowbit_name); } flowbit_total_match++; } /* direction: both */ if ( rulestruct[rule_position].flowbit_direction[i] == 1 && flowbit_ipc[a].ip_src == ip_src && flowbit_ipc[a].ip_dst == ip_dst ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] \"isset\" flowbit \"%s\" (direction: \"both\"). (%s -> %s)", __FILE__, __LINE__, flowbit_ipc[a].flowbit_name, ip_src_char, ip_dst_char); } flowbit_total_match++; } /* direction: by_src */ if ( rulestruct[rule_position].flowbit_direction[i] == 2 && flowbit_ipc[a].ip_src == ip_src ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] \"isset\" flowbit \"%s\" (direction: \"by_src\"). (%s -> any)", __FILE__, __LINE__, flowbit_ipc[a].flowbit_name, ip_src_char); } flowbit_total_match++; } /* direction: by_dst */ if ( rulestruct[rule_position].flowbit_direction[i] == 3 && flowbit_ipc[a].ip_dst == ip_dst ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] \"isset\" flowbit \"%s\" (direction: \"by_dst\"). (any -> %s)", __FILE__, __LINE__, flowbit_ipc[a].flowbit_name, ip_dst_char); } flowbit_total_match++; } /* direction: reverse */ if ( rulestruct[rule_position].flowbit_direction[i] == 4 && flowbit_ipc[a].ip_src == ip_dst && flowbit_ipc[a].ip_dst == ip_src ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] \"isset\" flowbit \"%s\" (direction: \"reverse\"). (%s -> %s)", __FILE__, __LINE__, flowbit_ipc[a].flowbit_name, ip_dst_char, ip_src_char); } flowbit_total_match++; } } /* End of strcmp flowbit_name & flowbit_state = 1 */ if ( and_or == 1) { tmp_flowbit_name = strtok_r(NULL, "|", &tok); } else { tmp_flowbit_name = strtok_r(NULL, "&", &tok); } } /* End of "while tmp_flowbit_name" */ } /* End of "for a" */ } /* End "if" flowbit_type == 3 (ISSET) */ /******************* * ISNOTSET * *******************/ if ( rulestruct[rule_position].flowbit_type[i] == 4 ) { flowbit_match = 0; for (a = 0; a < counters_ipc->flowbit_count; a++) { strlcpy(tmp, rulestruct[rule_position].flowbit_name[i], sizeof(tmp)); if (Sagan_strstr(rulestruct[rule_position].flowbit_name[i], "|")) { tmp_flowbit_name = strtok_r(tmp, "|", &tok); and_or = 1; } else { tmp_flowbit_name = strtok_r(tmp, "&", &tok); and_or = 0; /* Need this? */ } while (tmp_flowbit_name != NULL ) { if (!strcmp(tmp_flowbit_name, flowbit_ipc[a].flowbit_name)) { flowbit_match=1; if ( flowbit_ipc[a].flowbit_state == 0 ) { /* direction: none */ if ( rulestruct[rule_position].flowbit_direction[i] == 0 ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] \"isnotset\" flowbit \"%s\" (direction: \"none\"). (any -> any)", __FILE__, __LINE__, flowbit_ipc[a].flowbit_name); } flowbit_total_match++; } /* direction: both */ if ( rulestruct[rule_position].flowbit_direction[i] == 1 ) { if ( flowbit_ipc[a].ip_src == ip_src && flowbit_ipc[a].ip_dst == ip_dst ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] \"isnotset\" flowbit \"%s\" (direction: \"both\"). (%s -> %s)", __FILE__, __LINE__, flowbit_ipc[a].flowbit_name, ip_src_char, ip_dst_char); } flowbit_total_match++; } } /* direction: by_src */ if ( rulestruct[rule_position].flowbit_direction[i] == 2 ) { if ( flowbit_ipc[a].ip_src == ip_src ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] \"isnotset\" flowbit \"%s\" (direction: \"by_src\"). (%s -> any)", __FILE__, __LINE__, flowbit_ipc[a].flowbit_name, ip_src_char); } flowbit_total_match++; } } /* direction: by_dst */ if ( rulestruct[rule_position].flowbit_direction[i] == 3 ) { if ( flowbit_ipc[a].ip_dst == ip_dst ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] \"isnotset\" flowbit \"%s\" (direction: \"by_dst\"). (any -> %s)", __FILE__, __LINE__, flowbit_ipc[a].flowbit_name, ip_dst_char); } flowbit_total_match++; } } /* direction: reverse */ if ( rulestruct[rule_position].flowbit_direction[i] == 4 ) { if ( flowbit_ipc[a].ip_src == ip_dst && flowbit_ipc[a].ip_dst == ip_src ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] \"isnotset\" flowbit \"%s\" (direction: \"reverse\"). (%s -> %s)", __FILE__, __LINE__, flowbit_ipc[a].flowbit_name, ip_dst_char, ip_src_char); } flowbit_total_match++; } } } /* End flowbit_state == 0 */ } /* End of strcmp flowbit_name */ if ( and_or == 1) { tmp_flowbit_name = strtok_r(NULL, "|", &tok); } else { tmp_flowbit_name = strtok_r(NULL, "&", &tok); } } /* End of "while tmp_flowbit_name" */ } /* End of "for a" */ if ( and_or == 1 && flowbit_match == 1 ) { flowbit_total_match = rulestruct[rule_position].flowbit_condition_count; /* Do we even need this for OR? */ } if ( and_or == 0 && flowbit_match == 0 ) { flowbit_total_match = rulestruct[rule_position].flowbit_condition_count; } } /* End of "flowbit_type[i] == 4" */ } /* End of "for i" */ /* IF we match all criteria for isset/isnotset * * If we match the flowbit_conditon_count (number of concurrent flowbits) * we trigger. It it's an "or" statement, we trigger if any of the * flowbits are set. * */ if ( ( rulestruct[rule_position].flowbit_condition_count == flowbit_total_match ) || ( and_or == 1 && flowbit_total_match != 0 ) ) { if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] Condition of flowbit returning TRUE. %d %d", __FILE__, __LINE__, rulestruct[rule_position].flowbit_condition_count, flowbit_total_match); } return(true); } /* isset/isnotset failed. */ if ( debug->debugflowbit) { Sagan_Log(S_DEBUG, "[%s, line %d] Condition of flowbit returning FALSE. %d %d", __FILE__, __LINE__, rulestruct[rule_position].flowbit_condition_count, flowbit_total_match); } return(false); } /* End of Sagan_Flowbit_Condition(); */
void Sagan_Processor ( void ) { struct _Sagan_Proc_Syslog *SaganProcSyslog_LOCAL = NULL; SaganProcSyslog_LOCAL = malloc(sizeof(struct _Sagan_Proc_Syslog)); if ( SaganProcSyslog_LOCAL == NULL ) { Sagan_Log(S_ERROR, "[%s, line %d] Failed to allocate memory for SaganProcSyslog_LOCAL. Abort!", __FILE__, __LINE__); } memset(SaganProcSyslog_LOCAL, 0, sizeof(struct _Sagan_Proc_Syslog)); sbool ignore_flag=0; int i; for (;;) { pthread_mutex_lock(&SaganProcWorkMutex); while ( proc_msgslot == 0 ) pthread_cond_wait(&SaganProcDoWork, &SaganProcWorkMutex); if ( config->sagan_reload == 1 ) { pthread_cond_wait(&SaganReloadCond, &SaganReloadMutex); } proc_msgslot--; /* This was ++ before coming over, so we now -- it to get to * original value */ strlcpy(SaganProcSyslog_LOCAL->syslog_host, SaganProcSyslog[proc_msgslot].syslog_host, sizeof(SaganProcSyslog_LOCAL->syslog_host)); strlcpy(SaganProcSyslog_LOCAL->syslog_facility, SaganProcSyslog[proc_msgslot].syslog_facility, sizeof(SaganProcSyslog_LOCAL->syslog_facility)); strlcpy(SaganProcSyslog_LOCAL->syslog_priority, SaganProcSyslog[proc_msgslot].syslog_priority, sizeof(SaganProcSyslog_LOCAL->syslog_priority)); strlcpy(SaganProcSyslog_LOCAL->syslog_level, SaganProcSyslog[proc_msgslot].syslog_level, sizeof(SaganProcSyslog_LOCAL->syslog_level)); strlcpy(SaganProcSyslog_LOCAL->syslog_tag, SaganProcSyslog[proc_msgslot].syslog_tag, sizeof(SaganProcSyslog_LOCAL->syslog_tag)); strlcpy(SaganProcSyslog_LOCAL->syslog_date, SaganProcSyslog[proc_msgslot].syslog_date, sizeof(SaganProcSyslog_LOCAL->syslog_date)); strlcpy(SaganProcSyslog_LOCAL->syslog_time, SaganProcSyslog[proc_msgslot].syslog_time, sizeof(SaganProcSyslog_LOCAL->syslog_time)); strlcpy(SaganProcSyslog_LOCAL->syslog_program, SaganProcSyslog[proc_msgslot].syslog_program, sizeof(SaganProcSyslog_LOCAL->syslog_program)); strlcpy(SaganProcSyslog_LOCAL->syslog_message, SaganProcSyslog[proc_msgslot].syslog_message, sizeof(SaganProcSyslog_LOCAL->syslog_message)); pthread_mutex_unlock(&SaganProcWorkMutex); /* Check for general "drop" items. We do this first so we can save CPU later */ if ( config->sagan_droplist_flag ) { ignore_flag=0; for (i = 0; i < counters->droplist_count; i++) { if (Sagan_strstr(SaganProcSyslog_LOCAL->syslog_message, SaganIgnorelist[i].ignore_string)) { pthread_mutex_lock(&SaganIgnoreCounter); counters->ignore_count++; pthread_mutex_unlock(&SaganIgnoreCounter); ignore_flag=1; goto outside_loop; /* Stop processing from ignore list */ } } } outside_loop: /* If we're in a ignore state, then we can bypass the processors */ if ( ignore_flag == 0 ) { Sagan_Engine(SaganProcSyslog_LOCAL); if ( config->sagan_track_clients_flag ) { Sagan_Track_Clients( IP2Bit(SaganProcSyslog_LOCAL->syslog_host) ); } } // End if if (ignore_Flag) } // for (;;) Sagan_Log(S_WARN, "[%s, line %d] Holy cow! You should never see this message!", __FILE__, __LINE__); free(SaganProcSyslog_LOCAL); /* Should never make it here */ }
int main(int argc, char **argv) { const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "debug", required_argument, NULL, 'd' }, { "daemon", no_argument, NULL, 'D' }, { "user", required_argument, NULL, 'u' }, { "chroot", required_argument, NULL, 'c' }, { "credits", no_argument, NULL, 'C' }, { "config", required_argument, NULL, 'f' }, { "log", required_argument, NULL, 'l' }, { "file", required_argument, NULL, 'F' }, {0, 0, 0, 0} }; static const char *short_options = "l:f:u:d:c:pDhC"; int option_index = 0; /****************************************************************************/ /* libpcap/PLOG (syslog sniffer) local variables */ /****************************************************************************/ #ifdef HAVE_LIBPCAP pthread_t pcap_thread; pthread_attr_t thread_pcap_attr; pthread_attr_init(&thread_pcap_attr); pthread_attr_setdetachstate(&thread_pcap_attr, PTHREAD_CREATE_DETACHED); #endif /****************************************************************************/ /* Perfmonitor local variables */ /****************************************************************************/ pthread_t perfmonitor_thread; pthread_attr_t thread_perfmonitor_attr; pthread_attr_init(&thread_perfmonitor_attr); pthread_attr_setdetachstate(&thread_perfmonitor_attr, PTHREAD_CREATE_DETACHED); /****************************************************************************/ /* Various local variables */ /****************************************************************************/ /* Block all signals, we create a signal handling thread */ sigset_t signal_set; pthread_t sig_thread; sigfillset( &signal_set ); pthread_sigmask( SIG_BLOCK, &signal_set, NULL ); /* Key board handler (displays stats, etc */ pthread_t key_thread; pthread_attr_t key_thread_attr; pthread_attr_init(&key_thread_attr); pthread_attr_setdetachstate(&key_thread_attr, PTHREAD_CREATE_DETACHED); struct sockaddr_in sa; char src_dns_lookup[20]; int dns_flag=0; sbool fifoerr=0; char *syslog_host=NULL; char *syslog_facility=NULL; char *syslog_priority=NULL; char *syslog_level=NULL; char *syslog_tag=NULL; char *syslog_date=NULL; char *syslog_time=NULL; char *syslog_program=NULL; char *syslog_msg=NULL; char syslogstring[MAX_SYSLOGMSG]; signed char c; char *tok; int rc=0; int i; time_t t; struct tm *run; sbool debugflag=0; /* Allocate and clear memory for global structs */ /* Allocate memory for global struct _SaganDebug */ debug = malloc(sizeof(_SaganDebug)); memset(debug, 0, sizeof(_SaganDebug)); /* Allocate memroy for global struct _SaganConfig */ config = malloc(sizeof(_SaganConfig)); memset(config, 0, sizeof(_SaganConfig)); struct _SaganSigArgs *sigargs; sigargs = malloc(sizeof(_SaganSigArgs)); memset(sigargs, 0, sizeof(_SaganSigArgs)); struct _SaganDNSCache *dnscache; dnscache = malloc(sizeof(_SaganDNSCache)); memset(dnscache, 0, sizeof(_SaganDNSCache)); counters = malloc(sizeof(_SaganCounters)); memset(counters, 0, sizeof(_SaganCounters)); flowbit = malloc(sizeof(_Sagan_Flowbit)); memset(flowbit, 0, sizeof(_Sagan_Flowbit)); t = time(NULL); run=localtime(&t); strftime(config->sagan_startutime, sizeof(config->sagan_startutime), "%s", run); strlcpy(config->sagan_config, CONFIG_FILE_PATH, sizeof(config->sagan_config)); /* We set the config->sagan_log_filepath to the system default. It'll be fopen'ed shortly - 06/03/2011 - Champ Clark III */ strlcpy(config->sagan_log_filepath, SAGANLOG, sizeof(config->sagan_log_filepath)); config->sagan_runas = RUNAS; /* Get command line arg's */ while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) { switch(c) { if (c == -1) break; case 'h': Sagan_Usage(); exit(0); break; case 'C': Sagan_Credits(); exit(0); break; case 'd': if (Sagan_strstr(optarg, "malformed")) { debug->debugmalformed=1; debugflag=1; } if (Sagan_strstr(optarg, "limits")) { debug->debuglimits=1; debugflag=1; } if (Sagan_strstr(optarg, "syslog")) { debug->debugsyslog=1; debugflag=1; } if (Sagan_strstr(optarg, "load")) { debug->debugload=1; debugflag=1; } if (Sagan_strstr(optarg, "fwsam")) { debug->debugfwsam=1; debugflag=1; } if (Sagan_strstr(optarg, "external")) { debug->debugexternal=1; debugflag=1; } if (Sagan_strstr(optarg, "threads")) { debug->debugthreads=1; debugflag=1; } if (Sagan_strstr(optarg, "flowbit")) { debug->debugflowbit=1; debugflag=1; } if (Sagan_strstr(optarg, "engine")) { debug->debugengine=1; debugflag=1; } if (Sagan_strstr(optarg, "brointel")) { debug->debugbrointel=1; debugflag=1; } #ifdef HAVE_LIBGEOIP if (Sagan_strstr(optarg, "geoip")) { debug->debuggeoip=1; debugflag=1; } #endif #ifdef HAVE_LIBLOGNORM if (Sagan_strstr(optarg, "normalize" )) { debug->debugnormalize=1; debugflag=1; } #endif #ifdef HAVE_LIBESMTP if (Sagan_strstr(optarg, "smtp")) { debug->debugesmtp=1; debugflag=1; } #endif #ifdef HAVE_LIBPCAP if (Sagan_strstr(optarg, "plog")) { debug->debugplog=1; debugflag=1; } #endif #ifdef WITH_WEBSENSE if (Sagan_strstr(optarg, "websense")) { debug->debugwebsense=1; debugflag=1; } #endif /* If option is unknown */ if ( debugflag == 0 ) { fprintf(stderr, "Unknown debug option %s!\n", optarg); exit(1); } break; case 'D': daemonize=1; break; case 'u': config->sagan_runas=optarg; break; case 'c': Sagan_Chroot(optarg); break; case 'F': config->sagan_fifo_flag=1; strlcpy(config->sagan_fifo,optarg,sizeof(config->sagan_fifo) - 1); break; case 'f': strlcpy(config->sagan_config,optarg,sizeof(config->sagan_config) - 1); break; case 'l': strlcpy(config->sagan_log_filepath,optarg,sizeof(config->sagan_log_filepath) - 1); break; default: fprintf(stderr, "Invalid argument! See below for command line switches.\n"); Sagan_Usage(); exit(0); break; } } Sagan_Open_Log_File(OPEN, SAGAN_LOG); Load_Config(); Sagan_Engine_Init(); #ifdef HAVE_LIBLOGNORM Sagan_Liblognorm_Load(); #endif /* Load/init liblognorm definitions. I tried to move this into a subroutine, * but that ended up causing segfaults on ln_normalize() or causing * liblognorm not to function correctly (not parsing fields). Make reloading * a SIGHUP a issue as well. * 12/17/2010 - Champ */ SaganProcSyslog = malloc(config->max_processor_threads * sizeof(struct _Sagan_Proc_Syslog)); pthread_t processor_id[config->max_processor_threads]; pthread_attr_t thread_processor_attr; pthread_attr_init(&thread_processor_attr); pthread_attr_setdetachstate(&thread_processor_attr, PTHREAD_CREATE_DETACHED); Sagan_Log(S_NORMAL, "Configuration file %s loaded and %d rules loaded.", config->sagan_config, counters->rulecount); Sagan_Log(S_NORMAL, "Out of %d rules, %d Flowbit(s) are in use.", counters->rulecount, counters->flowbit_total_counter); Sagan_Log(S_NORMAL, "Sagan version %s is firing up!", VERSION); /* We go ahead and assign values to SaganSigArgs (struct sig_thread_args). This * struct is always used by the sig_handler thread, and sometimes used by the * plog_handler (below). So we assign values now */ sigargs->daemonize = daemonize; #ifdef HAVE_LIBPCAP /* Spawn a thread to 'sniff' syslog traffic (sagan-plog.c). This redirects syslog traffic to the /dev/log socket. This needs "root" access, so we drop priv's after this thread is started */ if ( config->plog_flag ) { rc = pthread_create( &pcap_thread, NULL, (void *)plog_handler, sigargs ); if ( rc != 0 ) { Remove_Lock_File(); Sagan_Log(S_ERROR, "[%s, line %d] Error creating libpcap handler thread [error: %d].", __FILE__, __LINE__, rc); } sleep(1); /* Sleep to avoid race between main() and plog thread plog thread needs "root" rights before sagan_droppriv(). In some cases main() run sagan_droppriv() before thread can complete - Champ Clark - 07/20/2011 */ } #endif Sagan_Droppriv(); /* Become the Sagan user */ Sagan_Log(S_NORMAL, "---------------------------------------------------------------------------"); if ( config->perfmonitor_flag ) { if (( config->perfmonitor_file_stream = fopen(config->perfmonitor_file_name, "a" )) == NULL ) { Remove_Lock_File(); Sagan_Log(S_ERROR, "[%s, line %d] Can't open %s - %s!", __FILE__, __LINE__, config->perfmonitor_file_name, strerror(errno)); } rc = pthread_create( &perfmonitor_thread, NULL, (void *)Sagan_Perfmonitor_Handler, sigargs ); if ( rc != 0 ) { Remove_Lock_File(); Sagan_Log(S_ERROR, "[%s, line %d] Error creating Perfmonitor thread [error: %d].", __FILE__, __LINE__, rc); } } /* Open sagan alert file */ Sagan_Open_Log_File(OPEN, ALERT_LOG); /**************************************************************************** * Display processor information as we load ****************************************************************************/ /* Sagan_Track_Clients processor ********************************************/ if ( config->sagan_track_clients_flag) { if ( config->pp_sagan_track_clients ) Sagan_Log(S_NORMAL, "Client Tracking Processor: %d minute(s)", config->pp_sagan_track_clients); Sagan_Track_Clients_Init(); Sagan_Load_Tracking_Cache(); } /* Sagan Blacklist IP processor *********************************************/ if ( config->blacklist_flag) { Sagan_Blacklist_Init(); Sagan_Blacklist_Load(); } /* Sagan Websense processor ************************************************/ #ifdef WITH_WEBSENSE if ( config->websense_flag ) { curl_global_init(CURL_GLOBAL_ALL); config->websense_last_time = atol(config->sagan_startutime); Sagan_Websense_Init(); Sagan_Log(S_NORMAL, ""); Sagan_Log(S_NORMAL, "Websense URL: %s", config->websense_url); Sagan_Log(S_NORMAL, "Websense Auth: %s", config->websense_auth); Sagan_Log(S_NORMAL, "Websense Device ID: %s", config->websense_device_id); Sagan_Log(S_NORMAL, "Websense Categories File: %s", config->websense_cat); Sagan_Log(S_NORMAL, "Websense Max Cache: %d", config->websense_max_cache); Sagan_Log(S_NORMAL, "Websense Cache Timeout: %d minutes.", config->websense_timeout / 60); Sagan_Log(S_NORMAL, "Websense loaded %d categories.", counters->websense_cat_count); } #endif /* Sagan Bro Intel processor *******************************************/ if ( config->brointel_flag ) { Sagan_Log(S_NORMAL, ""); Sagan_BroIntel_Init(); Sagan_BroIntel_Load_File(); Sagan_Log(S_NORMAL, "Bro Intel::ADDR Loaded: %d", counters->brointel_addr_count); Sagan_Log(S_NORMAL, "Bro Intel::DOMAIN Loaded: %d", counters->brointel_domain_count); Sagan_Log(S_NORMAL, "Bro Intel::FILE_HASH Loaded: %d", counters->brointel_file_hash_count); Sagan_Log(S_NORMAL, "Bro Intel::URL Loaded: %d", counters->brointel_url_count); Sagan_Log(S_NORMAL, "Bro Intel::SOFTWARE Loaded: %d", counters->brointel_software_count); Sagan_Log(S_NORMAL, "Bro Intel::EMAIL Loaded: %d", counters->brointel_email_count); Sagan_Log(S_NORMAL, "Bro Intel::USER_NAME Loaded: %d", counters->brointel_user_name_count); Sagan_Log(S_NORMAL, "Bro Intel::FILE_NAME Loaded: %d", counters->brointel_file_name_count); Sagan_Log(S_NORMAL, "Bro Intel::CERT_HASH Loaded: %d", counters->brointel_cert_hash_count); Sagan_Log(S_NORMAL, "Bro Intel Duplicates Detected: %d", counters->brointel_dups); } /*************************************************************************** * Output plugins ***************************************************************************/ #ifdef HAVE_LIBESMTP if ( config->sagan_esmtp_flag ) { Sagan_Log(S_NORMAL, ""); if ( config->min_email_priority ) Sagan_Log(S_NORMAL, "E-mail on priority %d or higher.", config->min_email_priority); Sagan_Log(S_NORMAL, "E-Mail will be sent from: %s", config->sagan_esmtp_from); Sagan_Log(S_NORMAL, "SMTP server is set to: %s", config->sagan_esmtp_server); } #endif #ifdef WITH_SNORTSAM if ( config->sagan_fwsam_flag ) { Sagan_Log(S_NORMAL, ""); Sagan_Log(S_NORMAL, "Snortsam output plug in enabled."); } #endif if ( config->sagan_external_output_flag ) { Sagan_Log(S_NORMAL, ""); Sagan_Log(S_NORMAL, "External program to be called: %s", config->sagan_extern); } /* Unified2 ****************************************************************/ #if defined(HAVE_DNET_H) || defined(HAVE_DUMBNET_H) if ( config->sagan_unified2_flag ) { Sagan_Log(S_NORMAL, ""); Sagan_Log(S_NORMAL, "Unified2 file: %s", config->unified2_filepath); Sagan_Log(S_NORMAL, "Unified2 limit: %dM", config->unified2_limit / 1024 / 1024 ); Unified2InitFile(); } #endif /*************************************************************************** * Non-Processor/Output option ***************************************************************************/ /* What to "ignore" ********************************************************/ if ( config->sagan_droplist_flag ) { Load_Ignore_List(); Sagan_Log(S_NORMAL, ""); Sagan_Log(S_NORMAL, "Loaded %d ignore/drop list item(s).", counters->droplist_count); } /*************************************************************************** * Continue with normal startup! ***************************************************************************/ Sagan_Log(S_NORMAL, ""); Sagan_Log(S_NORMAL, " ,-._,-. -*> Sagan! <*-"); Sagan_Log(S_NORMAL, " \\/)\"(\\/ Version %s", VERSION); Sagan_Log(S_NORMAL, " (_o_) Champ Clark III & The Quadrant InfoSec Team [quadrantsec.com]"); Sagan_Log(S_NORMAL, " / \\/) Copyright (C) 2009-2015 Quadrant Information Security, et al."); Sagan_Log(S_NORMAL, " (|| ||) Using PCRE version: %s", pcre_version()); Sagan_Log(S_NORMAL, " oo-oo Sagan is processing events....."); Sagan_Log(S_NORMAL, ""); /* Become a daemon if requested */ if ( daemonize ) { Sagan_Log(S_NORMAL, "Becoming a daemon!"); pid_t pid = 0; setsid(); pid = fork(); if (pid == 0) {} else { exit(0); } } /* Create the signal handlers thread _after_ the fork() so it can properly * handly signals - Champ Clark III - 06/13/2011 */ rc = pthread_create( &sig_thread, NULL, (void *)Sig_Handler, sigargs ); if ( rc != 0 ) { Remove_Lock_File(); Sagan_Log(S_ERROR, "[%s, line %d] Error creating signal handler thread. [error: %d]", __FILE__, __LINE__, rc); } /* We don't want the key_handler() if we're in daemon mode! */ if (!daemonize) { rc = pthread_create( &key_thread, NULL, (void *)key_handler, NULL ); if ( rc != 0 ) { Remove_Lock_File(); Sagan_Log(S_ERROR, "[%s, line %d] Error creating key_handler thread. [error: %d]", __FILE__, __LINE__, rc); } } /* We do this after forking so init scripts can complete */ /* Check lock file _after_ thread. If you don't it'll retreive the wrong pid * and incorrectly believe there is a stale lock file if --daemon */ checklockfile(); Sagan_Log(S_NORMAL, "Spawning %d Processor Threads.", config->max_processor_threads); for (i = 0; i < config->max_processor_threads; i++) { rc = pthread_create ( &processor_id[i], &thread_processor_attr, (void *)Sagan_Processor, NULL ); if ( rc != 0 ) { Remove_Lock_File(); Sagan_Log(S_ERROR, "Could not pthread_create() for I/O processors [error: %d]", rc); } } Sagan_Log(S_NORMAL, ""); if ( config->sagan_fifo_flag == 0 ) { Sagan_Log(S_NORMAL, "Attempting to open syslog FIFO (%s).", config->sagan_fifo); } else { Sagan_Log(S_NORMAL, "Attempting to open syslog FILE (%s).", config->sagan_fifo); } while(1) { FILE *fd; fd = fopen(config->sagan_fifo, "r"); if ( config->sagan_fifo_flag == 0 ) { Sagan_Log(S_NORMAL, "Successfully opened FIFO (%s).", config->sagan_fifo); } else { Sagan_Log(S_NORMAL, "Successfully opened FILE (%s) and processing events.....", config->sagan_fifo); } while(fd != NULL) { while(fgets(syslogstring, sizeof(syslogstring), fd) != NULL) { /* If the FIFO was in a error state, let user know the FIFO writer has resumed */ if ( fifoerr == 1 ) { Sagan_Log(S_NORMAL, "FIFO writer has restarted. Processing events."); fifoerr=0; } counters->sagantotal++; syslog_host = strtok_r(syslogstring, "|", &tok); /* If we're using DNS (and we shouldn't be!), we start DNS checks and lookups * here. We cache both good and bad lookups to not over load our DNS server(s). * The only way DNS cache can be cleared is to restart Sagan */ if (config->syslog_src_lookup ) { if ( inet_pton(AF_INET, syslog_host, &(sa.sin_addr)) == 0 ) /* Is inbound a valid IP? */ { dns_flag=0; for(i=0; i <= counters->dns_cache_count ; i++) /* Check cache first */ { if (!strcmp( dnscache[i].hostname, syslog_host)) { syslog_host = dnscache[i].src_ip; dns_flag=1; } } /* If entry was not found in cache, look it up */ if ( dns_flag == 0 ) { /* Do a DNS lookup */ strlcpy(src_dns_lookup, DNS_Lookup(syslog_host), sizeof(src_dns_lookup)); /* Invalid lookups get the config->sagan_host value */ if (src_dns_lookup[0] == '0' ) { strlcpy(src_dns_lookup, config->sagan_host, sizeof(src_dns_lookup)); counters->dns_miss_count++; } /* Add entry to DNS Cache */ dnscache = (_SaganDNSCache *) realloc(dnscache, (counters->dns_cache_count+1) * sizeof(_SaganDNSCache)); strlcpy(dnscache[counters->dns_cache_count].hostname, syslog_host, sizeof(dnscache[counters->dns_cache_count].hostname)); strlcpy(dnscache[counters->dns_cache_count].src_ip, src_dns_lookup, sizeof(dnscache[counters->dns_cache_count].src_ip)); counters->dns_cache_count++; syslog_host = src_dns_lookup; } } } else { /* We check to see if values from our FIFO are valid. If we aren't doing DNS related * stuff (above), we start basic check with the syslog_host */ if (syslog_host == NULL || inet_pton(AF_INET, syslog_host, &(sa.sin_addr)) == 0 ) { syslog_host = config->sagan_host; pthread_mutex_lock(&SaganMalformedCounter); counters->malformed_host++; pthread_mutex_unlock(&SaganMalformedCounter); if ( debug->debugmalformed ) { Sagan_Log(S_WARN, "Sagan received a malformed 'host' (replaced with %s)", config->sagan_host); } } } // if ( config->home_any == 1) { /* We know check the rest of the values */ syslog_facility=strtok_r(NULL, "|", &tok); if ( syslog_facility == NULL ) { syslog_facility = "SAGAN: FACILITY ERROR"; pthread_mutex_lock(&SaganMalformedCounter); counters->malformed_facility++; pthread_mutex_unlock(&SaganMalformedCounter); if ( debug->debugmalformed ) { Sagan_Log(S_WARN, "Sagan received a malformed 'facility'"); } } syslog_priority=strtok_r(NULL, "|", &tok); if ( syslog_priority == NULL ) { syslog_priority = "SAGAN: PRIORITY ERROR"; pthread_mutex_lock(&SaganMalformedCounter); counters->malformed_priority++; pthread_mutex_unlock(&SaganMalformedCounter); if ( debug->debugmalformed ) { Sagan_Log(S_WARN, "Sagan received a malformed 'priority'"); } } syslog_level=strtok_r(NULL, "|", &tok); if ( syslog_level == NULL ) { syslog_level = "SAGAN: LEVEL ERROR"; pthread_mutex_lock(&SaganMalformedCounter); counters->malformed_level++; pthread_mutex_unlock(&SaganMalformedCounter); if ( debug->debugmalformed ) { Sagan_Log(S_WARN, "Sagan received a malformed 'level'"); } } syslog_tag=strtok_r(NULL, "|", &tok); if ( syslog_tag == NULL ) { syslog_tag = "SAGAN: TAG ERROR"; pthread_mutex_lock(&SaganMalformedCounter); counters->malformed_tag++; pthread_mutex_unlock(&SaganMalformedCounter); if ( debug->debugmalformed ) { Sagan_Log(S_WARN, "Sagan received a malformed 'tag'"); } } syslog_date=strtok_r(NULL, "|", &tok); if ( syslog_date == NULL ) { syslog_date = "SAGAN: DATE ERROR"; pthread_mutex_lock(&SaganMalformedCounter); counters->malformed_date++; pthread_mutex_unlock(&SaganMalformedCounter); if ( debug->debugmalformed ) { Sagan_Log(S_WARN, "Sagan received a malformed 'date'"); } } syslog_time=strtok_r(NULL, "|", &tok); if ( syslog_time == NULL ) { syslog_time = "SAGAN: TIME ERROR"; pthread_mutex_lock(&SaganMalformedCounter); counters->malformed_time++; pthread_mutex_unlock(&SaganMalformedCounter); if ( debug->debugmalformed ) { Sagan_Log(S_WARN, "Sagan received a malformed 'time'"); } } syslog_program=strtok_r(NULL, "|", &tok); if ( syslog_program == NULL ) { syslog_program = "SAGAN: PROGRAM ERROR"; pthread_mutex_lock(&SaganMalformedCounter); counters->malformed_program++; pthread_mutex_unlock(&SaganMalformedCounter); if ( debug->debugmalformed ) { Sagan_Log(S_WARN, "Sagan received a malformed 'program'"); } } syslog_msg=strtok_r(NULL, "", &tok); /* In case the message has | in it, we delimit on "" */ if ( syslog_msg == NULL ) { syslog_msg = "SAGAN: MESSAGE ERROR"; pthread_mutex_lock(&SaganMalformedCounter); counters->malformed_message++; pthread_mutex_unlock(&SaganMalformedCounter); if ( debug->debugmalformed ) { Sagan_Log(S_WARN, "Sagan received a malformed 'message' [Syslog Host: %s]", syslog_host); } /* If the message is lost, all is lost. Typically, you don't lose part of the message, * it's more likely to lose all - Champ Clark III 11/17/2011 */ counters->sagan_log_drop++; } /* Strip any \n or \r from the syslog_msg */ if ( strcspn ( syslog_msg, "\n" ) < strlen(syslog_msg) ) syslog_msg[strcspn ( syslog_msg, "\n" )] = '\0'; if ( proc_msgslot < config->max_processor_threads ) { pthread_mutex_lock(&SaganProcWorkMutex); strlcpy(SaganProcSyslog[proc_msgslot].syslog_host, syslog_host, sizeof(SaganProcSyslog[proc_msgslot].syslog_host)); strlcpy(SaganProcSyslog[proc_msgslot].syslog_facility, syslog_facility, sizeof(SaganProcSyslog[proc_msgslot].syslog_facility)); strlcpy(SaganProcSyslog[proc_msgslot].syslog_priority, syslog_priority, sizeof(SaganProcSyslog[proc_msgslot].syslog_priority)); strlcpy(SaganProcSyslog[proc_msgslot].syslog_level, syslog_level, sizeof(SaganProcSyslog[proc_msgslot].syslog_level)); strlcpy(SaganProcSyslog[proc_msgslot].syslog_tag, syslog_tag, sizeof(SaganProcSyslog[proc_msgslot].syslog_tag)); strlcpy(SaganProcSyslog[proc_msgslot].syslog_date, syslog_date, sizeof(SaganProcSyslog[proc_msgslot].syslog_date)); strlcpy(SaganProcSyslog[proc_msgslot].syslog_time, syslog_time, sizeof(SaganProcSyslog[proc_msgslot].syslog_time)); strlcpy(SaganProcSyslog[proc_msgslot].syslog_program, syslog_program, sizeof(SaganProcSyslog[proc_msgslot].syslog_program)); strlcpy(SaganProcSyslog[proc_msgslot].syslog_message, syslog_msg, sizeof(SaganProcSyslog[proc_msgslot].syslog_message)); proc_msgslot++; pthread_cond_signal(&SaganProcDoWork); pthread_mutex_unlock(&SaganProcWorkMutex); } else { counters->worker_thread_exhaustion++; counters->sagan_log_drop++; } if (debug->debugthreads) Sagan_Log(S_DEBUG, "Current \"proc_msgslot\": %d", proc_msgslot); if (debug->debugsyslog) { Sagan_Log(S_DEBUG, "[%s, line %d] **[RAW Syslog]*********************************", __FILE__, __LINE__); Sagan_Log(S_DEBUG, "[%s, line %d] Host: %s | Program: %s | Facility: %s | Priority: %s | Level: %s | Tag: %s", __FILE__, __LINE__, syslog_host, syslog_program, syslog_facility, syslog_priority, syslog_level, syslog_tag); Sagan_Log(S_DEBUG, "[%s, line %d] Raw message: %s", __FILE__, __LINE__, syslog_msg); } } /* while(fgets) */ /* fgets() has returned a error, likely due to the FIFO writer leaving */ /* DEBUG : set a kill flag and join */ /* RMEOVE LOCK */ if ( fifoerr == 0 ) { if ( config->sagan_fifo_flag != 0 ) { Sagan_Log(S_NORMAL, "EOF reached. Waiting for threads to catch up"); sleep(5); fclose(fd); Sagan_Log(S_NORMAL, "Exiting."); /* DEBUG: Rejoin threads */ exit(0); } else { Sagan_Log(S_WARN, "FIFO writer closed. Waiting for FIFO writer to restart...."); fifoerr=1; /* Set flag so our wile(fgets) knows */ } } sleep(1); /* So we don't eat 100% CPU */ } /* while(fd != NULL) */ fclose(fd); /* ???? */ } /* End of while(1) */ } /* End of main */