void plog_handler(_SaganSigArgs *args ) { pcap_t *bp; struct bpf_program filtr; char *iface=NULL; char eb[PCAP_ERRBUF_SIZE]; char filterstr[128]; iface = config->plog_interface; Sagan_Log(S_NORMAL, ""); Sagan_Log(S_NORMAL, "Initalizing Sagan syslog sniffer thread (PLOG)"); Sagan_Log(S_NORMAL, "Interface: %s", iface); Sagan_Log(S_NORMAL, "Packet filter: \"%s\"", config->plog_filter); Sagan_Log(S_NORMAL, "Log device: %s", config->plog_logdev); if ( config->plog_promiscuous ) { Sagan_Log(S_NORMAL, "Promiscuous is enabled."); } Sagan_Log(S_NORMAL, ""); if(iface == (char *)0) { if((iface = pcap_lookupdev(eb)) == (char *)0) Sagan_Log(S_ERROR, "[%s, line %d] Cannot get device: %s", __FILE__, __LINE__, eb); } bp = pcap_open_live(iface,4096,config->plog_promiscuous,0,eb); if(bp == (pcap_t *)0) Sagan_Log(S_ERROR, "[%s, line %d] Cannot open interface %s: %s", __FILE__, __LINE__, iface, eb); /* Apply user defined filter */ if(pcap_compile(bp,&filtr,config->plog_filter,1,0)) Sagan_Log(S_ERROR, "[%s, line %d] Cannot compile filter: %s", __FILE__, __LINE__, eb); if(pcap_setfilter(bp,&filtr)) Sagan_Log(S_ERROR, "[%s, line %d] Cannot install filter in %s: %s", __FILE__, __LINE__, iface, eb); /* wireup /dev/log; we can't use openlog() because these are going to be raw inputs */ if(wiredevlog(config)) { Remove_Lock_File(); Sagan_Log(S_ERROR, "[%s, line %d] Cannot open %s (Syslog not using SOCK_DGRAM?)", __FILE__, __LINE__, config->plog_logdev); } /* endless loop */ (void)pcap_loop(bp,-1,logpkt, (u_char*)args); pcap_close(bp); exit(0); }
void plog_handler(_SaganSigArgs *args ) { pcap_t *bp; struct bpf_program filtr; char *iface=NULL; char eb[PCAP_ERRBUF_SIZE]; char filterstr[128]; iface = config->plog_interface; Sagan_Log(0, ""); Sagan_Log(0, "Initalizing Sagan syslog sniffer thread (PLOG)"); Sagan_Log(0, "Interface: %s", iface); Sagan_Log(0, "UDP port to monitor: %d", config->plog_port); Sagan_Log(0, "Log device: %s", config->plog_logdev); Sagan_Log(0, ""); if(iface == (char *)0) { if((iface = pcap_lookupdev(eb)) == (char *)0) Sagan_Log(1, "[%s, line %d] Cannot get device: %s", __FILE__, __LINE__, eb); } bp = pcap_open_live(iface,4096,0,0,eb); if(bp == (pcap_t *)0) Sagan_Log(1, "[%s, line %d] Cannot open interface %s: %s", __FILE__, __LINE__, iface, eb); /* compile and install our filter */ /* Port is configurable via int config->plog_port */ snprintf(filterstr, sizeof(filterstr), "udp port %d", config->plog_port); if(pcap_compile(bp,&filtr,filterstr,1,0)) Sagan_Log(1, "[%s, line %d] Cannot compile filter: %s", __FILE__, __LINE__, eb); if(pcap_setfilter(bp,&filtr)) Sagan_Log(1, "[%s, line %d] Cannot install filter in %s: %s", __FILE__, __LINE__, iface, eb); /* wireup /dev/log; we can't use openlog() because these are going to be raw inputs */ if(wiredevlog(config)) { Remove_Lock_File(); Sagan_Log(1, "[%s, line %d] Cannot open %s (Syslog not using SOCK_DGRAM?)", __FILE__, __LINE__, config->plog_logdev); } /* endless loop */ (void)pcap_loop(bp,-1,logpkt, (u_char*)args); pcap_close(bp); exit(0); }
void Sagan_Ext_Thread ( _SaganEvent *Event, char *execute_script ) { int in[2]; int out[2]; int n, pid; char buf[MAX_SYSLOGMSG]; char data[MAX_SYSLOGMSG]; char *tmpref = NULL; char tmp[6]; if ( debug->debugexternal ) { Sagan_Log(S_WARN, "[%s, line %d] In sagan_ext_thread()", __FILE__, __LINE__); } tmpref = Reference_Lookup( Event->found, 1 ); if ( Event->drop == 1 ) { snprintf(tmp, sizeof(tmp), "True"); } else { snprintf(tmp, sizeof(tmp), "False"); } snprintf(data, sizeof(data), "\n\ ID:%lu:%s\n\ Message:%s\n\ Classification:%s\n\ Drop:%s\n\ Priority:%d\n\ Date:%s\n\ Time:%s\n\ Source:%s\n\ Source Port:%d\n\ Destination:%s\n\ Destination Port:%d\n\ Facility:%s\n\ Syslog Priority:%s\n\ %sSyslog message:%s\n"\ \ ,Event->generatorid\ ,Event->sid,\ Event->f_msg,\ Event->class,\ tmp,\ Event->pri,\ Event->date,\ Event->time,\ Event->ip_src,\ Event->src_port,\ Event->ip_dst,\ Event->dst_port,\ Event->facility,\ Event->priority,\ tmpref,\ Event->message); pthread_mutex_lock( &ext_mutex ); if ( pipe(in) < 0 ) { Remove_Lock_File(); Sagan_Log(S_ERROR, "[%s, line %d] Cannot create input pipe!", __FILE__, __LINE__); } if ( pipe(out) < 0 ) { Remove_Lock_File(); Sagan_Log(S_ERROR, "[%s, line %d] Cannot create output pipe!", __FILE__, __LINE__); } pid=fork(); if ( pid < 0 ) { Sagan_Log(S_ERROR, "[%s, line %d] Cannot create external program process", __FILE__, __LINE__); } else if ( pid == 0 ) { /* Causes problems with alert.log */ close(0); close(1); close(2); dup2(in[0],0); // Stdin.. dup2(out[1],1); dup2(out[1],2); close(in[1]); close(out[0]); //ret=execl(config->sagan_extern, config->sagan_extern, NULL, (char *)NULL); //execl(config->sagan_extern, config->sagan_extern, NULL, (char *)NULL); execl(execute_script, execute_script, NULL, (char *)NULL); Remove_Lock_File(); Sagan_Log(S_WARN, "[%s, line %d] Cannot execute %s", __FILE__, __LINE__, config->sagan_extern); } close(in[0]); close(out[1]); /* Write to child input */ n = write(in[1], data, strlen(data)); close(in[1]); n = read(out[0], buf, sizeof(buf)); close(out[0]); buf[n] = 0; waitpid(pid, NULL, 0); pthread_mutex_unlock( &ext_mutex ); if ( debug->debugexternal == 1 ) { Sagan_Log(S_DEBUG, "[%s, line %d] Executed %s", __FILE__, __LINE__, config->sagan_extern); } }
void Sagan_Open_Log_File( sbool state, int type ) { struct passwd *pw = NULL; int ret; pw = getpwnam(config->sagan_runas); if ( type == SAGAN_LOG || type == ALL_LOGS ) { /* For SIGHUP */ if ( state == REOPEN ) { fclose(config->sagan_log_stream); } if ((config->sagan_log_stream = fopen(config->sagan_log_filepath, "a")) == NULL) { fprintf(stderr, "[E] [%s, line %d] Cannot open %s - %s!\n", __FILE__, __LINE__, config->sagan_log_filepath, strerror(errno)); exit(1); } /* Chown the log files in case we get a SIGHUP or whatnot later (due to Sagan_Chroot()) */ ret = chown(config->sagan_log_filepath, (unsigned long)pw->pw_uid,(unsigned long)pw->pw_gid); if ( ret < 0 ) { Sagan_Log(S_ERROR, "[%s, line %d] Cannot change ownership of %s to username %s - %s", __FILE__, __LINE__, config->sagan_log_filepath, config->sagan_runas, strerror(errno)); } } if ( type == ALERT_LOG || type == ALL_LOGS ) { /* For SIGHUP */ if ( state == REOPEN ) { fclose(config->sagan_alert_stream); } if (( config->sagan_alert_stream = fopen(config->sagan_alert_filepath, "a" )) == NULL ) { Remove_Lock_File(); Sagan_Log(S_ERROR, "[%s, line %d] Can't open %s - %s!", __FILE__, __LINE__, config->sagan_alert_filepath, strerror(errno)); } /* Chown the log files in case we get a SIGHUP or whatnot later (due to Sagan_Chroot()) */ ret = chown(config->sagan_alert_filepath, (unsigned long)pw->pw_uid,(unsigned long)pw->pw_gid); if ( ret < 0 ) { Sagan_Log(S_ERROR, "[%s, line %d] Cannot change ownership of %s to username %s - %s", __FILE__, __LINE__, config->sagan_alert_filepath, config->sagan_runas, strerror(errno)); } } }
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 */
void Open_Log_File( bool state, int type ) { struct passwd *pw = NULL; pw = getpwnam(config->sagan_runas); if( pw == NULL) { fprintf(stderr, "[E] [%s, line %d] Invalid user %s (use -u option to set a user)\n", __FILE__, __LINE__, config->sagan_runas); exit(-1); } if ( type == SAGAN_LOG || type == ALL_LOGS ) { /* For SIGHUP */ if ( state == REOPEN ) { CloseStream(config->sagan_log_stream, &config->sagan_log_fd); } if ((config->sagan_log_stream = OpenStream(config->sagan_log_filepath, &config->sagan_log_fd,(unsigned long)pw->pw_uid,(unsigned long)pw->pw_gid)) == NULL) { fprintf(stderr, "[E] [%s, line %d] Cannot open %s - %s!\n", __FILE__, __LINE__, config->sagan_log_filepath, strerror(errno)); exit(-1); } config->sagan_log_stream_status = true; } if ( type == ALERT_LOG || type == ALL_LOGS ) { /* For SIGHUP */ if ( state == REOPEN && config->eve_flag == true ) { CloseStream(config->eve_stream, &config->eve_fd); } if ( state == REOPEN && config->alert_flag == true ) { CloseStream(config->sagan_alert_stream, &config->sagan_alert_fd); } if ( state == REOPEN && config->fast_flag == true ) { CloseStream(config->sagan_fast_stream, &config->sagan_fast_fd); } if ( config->eve_flag ) { if (( config->eve_stream = OpenStream(config->eve_filename, &config->eve_fd, (unsigned long)pw->pw_uid, (unsigned long)pw->pw_gid )) == NULL ) { Remove_Lock_File(); Sagan_Log(ERROR, "[%s, line %d] Can't open \"%s\" - %s!", __FILE__, __LINE__, config->eve_filename, strerror(errno)); } config->eve_stream_status = true; } if ( config->fast_flag ) { if (( config->sagan_fast_stream = OpenStream(config->fast_filename, &config->sagan_fast_fd, (unsigned long)pw->pw_uid, (unsigned long)pw->pw_gid )) == NULL ) { Remove_Lock_File(); Sagan_Log(ERROR, "[%s, line %d] Can't open %s - %s!", __FILE__, __LINE__, config->fast_filename, strerror(errno)); } config->sagan_fast_stream_status = true; } if ( config->alert_flag ) { if (( config->sagan_alert_stream = OpenStream(config->sagan_alert_filepath, &config->sagan_alert_fd, (unsigned long)pw->pw_uid, (unsigned long)pw->pw_gid )) == NULL ) { Remove_Lock_File(); Sagan_Log(ERROR, "[%s, line %d] Can't open %s - %s!", __FILE__, __LINE__, config->sagan_alert_filepath, strerror(errno)); } config->sagan_alert_stream_status = true; } } }
void Sig_Handler( _SaganSigArgs *args ) { sigset_t signal_set; int sig; sbool orig_perfmon_value = 0; #ifdef HAVE_LIBPCAP sbool orig_plog_value = 0; #endif for(;;) { /* wait for any and all signals */ sigfillset( &signal_set ); sigwait( &signal_set, &sig ); switch( sig ) { /* exit */ case SIGQUIT: case SIGINT: case SIGTERM: case SIGSEGV: case SIGABRT: Sagan_Log(S_NORMAL, "\n\n[Received signal %d. Sagan version %s shutting down]-------\n", sig, VERSION); Sagan_Statistics(); #if defined(HAVE_DNET_H) || defined(HAVE_DUMBNET_H) if ( sagan_unified2_flag ) { Unified2CleanExit(); } #endif #ifdef HAVE_LIBMAXMINDDB MMDB_close(&config->geoip2); #endif fflush(config->sagan_alert_stream); fclose(config->sagan_alert_stream); /* Close Sagan alert file */ fflush(config->sagan_log_stream); /* Close the sagan.log */ fclose(config->sagan_log_stream); if ( config->perfmonitor_flag ) { Sagan_Perfmonitor_Close(); } Remove_Lock_File(); exit(0); break; case SIGHUP: config->sagan_reload = 1; /* Only this thread can alter this */ pthread_mutex_lock(&SaganReloadMutex); Sagan_Log(S_NORMAL, "[Reloading Sagan version %s.]-------", VERSION); /* * Close and re-open log files. This is for logrotate and such * 04/14/2015 - Champ Clark III ([email protected]) */ Sagan_Open_Log_File(REOPEN, ALL_LOGS); /******************/ /* Reset counters */ /******************/ counters->refcount=0; counters->classcount=0; counters->rulecount=0; counters->ruletotal=0; counters->genmapcount=0; counters->flowbit_track_count=0; memset(rulestruct, 0, sizeof(_Rule_Struct)); memset(classstruct, 0, sizeof(_Class_Struct)); memset(generator, 0, sizeof(_Sagan_Processor_Generator)); memset(flowbit, 0, sizeof(_Sagan_Flowbit)); /**********************************/ /* Disabled and reset processors. */ /**********************************/ /* Note: Processors that run as there own thread (perfmon, plog) cannot be * loaded via SIGHUP. They must be loaded at run time. Once they are loaded, * they can be disabled/re-enabled. */ /* Single Threaded processors */ if ( config->perfmonitor_flag == 1 && orig_perfmon_value == 0 ) { Sagan_Perfmonitor_Close(); orig_perfmon_value = 1; } config->perfmonitor_flag = 0; #ifdef HAVE_LIBPCAP if ( config->plog_flag ) { orig_plog_value = 1; } config->plog_flag = 0; #endif /* Multi Threaded processors */ config->blacklist_flag = 0; if ( config->blacklist_flag ) { free(SaganBlacklist); } config->blacklist_flag = 0; if ( config->brointel_flag ) { free(Sagan_BroIntel_Intel_Addr); free(Sagan_BroIntel_Intel_Domain); free(Sagan_BroIntel_Intel_File_Hash); free(Sagan_BroIntel_Intel_URL); free(Sagan_BroIntel_Intel_Software); free(Sagan_BroIntel_Intel_Email); free(Sagan_BroIntel_Intel_User_Name); free(Sagan_BroIntel_Intel_File_Name); free(Sagan_BroIntel_Intel_Cert_Hash); counters->brointel_addr_count = 0; counters->brointel_domain_count = 0; counters->brointel_file_hash_count = 0; counters->brointel_url_count = 0; counters->brointel_software_count = 0; counters->brointel_email_count = 0; counters->brointel_user_name_count = 0; counters->brointel_file_name_count = 0; counters->brointel_cert_hash_count = 0; counters->brointel_dups = 0; } config->brointel_flag = 0; if ( config->sagan_track_clients_flag ) { free(SaganTrackClients); fclose(config->sagan_track_client_file); } config->sagan_track_clients_flag = 0; counters->track_clients_client_count = 0; counters->track_clients_down = 0; /* Output formats */ config->sagan_ext_flag = 0; #ifdef WITH_SYSLOG config->sagan_syslog_flag = 0; #endif #ifdef HAVE_LIBESMTP config->sagan_esmtp_flag = 0; #endif #ifdef WITH_SNORTSAM config->sagan_fwsam_flag = 0; #endif /* Non-output / Processors */ if ( config->sagan_droplist_flag ) { config->sagan_droplist_flag = 0; free(SaganIgnorelist); } /************************************************************/ /* Re-load primary configuration (rules/classifictions/etc) */ /************************************************************/ Load_Config(); /* <- RELOAD */ /************************************************************/ /* Re-load primary configuration (rules/classifictions/etc) */ /************************************************************/ if ( config->perfmonitor_flag == 1 ) { if ( orig_perfmon_value == 1 ) { Sagan_Perfmonitor_Open(); } else { Sagan_Log(S_WARN, "** 'perfmonitor' must be loaded at runtime! NOT loading 'perfmonitor'!"); config->perfmonitor_flag = 0; } } #ifdef HAVE_LIBPCAP if ( config->plog_flag == 1 ) { if ( orig_plog_value == 1 ) { config->plog_flag = 1; } else { Sagan_Log(S_WARN, "** 'plog' must be loaded at runtime! NOT loading 'plog'!"); config->plog_flag = 0; } } #endif /* Load Blacklist data */ if ( config->blacklist_flag ) { counters->blacklist_count=0; Sagan_Blacklist_Init(); Sagan_Blacklist_Load(); } if ( config->brointel_flag ) { Sagan_BroIntel_Init(); Sagan_BroIntel_Load_File(); } if ( config->sagan_track_clients_flag ) { Sagan_Load_Tracking_Cache(); Sagan_Log(S_NORMAL, "Reset Sagan Track Client."); } /* Non output / processors */ if ( config->sagan_droplist_flag ) { Load_Ignore_List(); Sagan_Log(S_NORMAL, "Loaded %d ignore/drop list item(s).", counters->droplist_count); } #ifdef HAVE_LIBMAXMINDDB Sagan_Log(S_NORMAL, "Reloading GeoIP2 data."); Sagan_Open_GeoIP2_Database(); #endif #ifdef HAVE_LIBLOGNORM Sagan_Liblognorm_Load(); #endif pthread_cond_signal(&SaganReloadCond); pthread_mutex_unlock(&SaganReloadMutex); config->sagan_reload = 0; Sagan_Log(S_NORMAL, "Configuration reloaded."); break; /* Signals to ignore */ case 17: /* Child process has exited. */ case 28: /* Terminal 'resize'/alarm. */ break; case SIGUSR1: Sagan_Statistics(); break; default: Sagan_Log(S_NORMAL, "[Received signal %d. Sagan doesn't know how to deal with]", sig); } } }