sec-2.7.8/0000755000175000017500000000000012623566122011371 5ustar ristoristosec-2.7.8/contrib/0000755000175000017500000000000012623126626013032 5ustar ristoristosec-2.7.8/contrib/suse.sysconfig0000644000175000017500000000050112263012625015724 0ustar ristoristo## Description: Simple Event Correlator Configuration File ## URL: http://simple-evcorr.sourceforge.net/ ## License: GPL-2.0 ## Modified by Malcolm J Lewis ## Path: System/sec ## Description: sec settings ## Type: string ## Default: "" ## ServiceRestart: sec # # Options for sec # SEC_OPTS="" sec-2.7.8/contrib/suse.service0000644000175000017500000000046612356754631015407 0ustar ristoristo[Unit] Description=Simple Event Correlator script for event log monitoring After=syslog.target [Service] Type=forking PIDFile=/run/sec.pid ExecStart=/bin/bash -c '[ -e /etc/sysconfig/sec ] && . /etc/sysconfig/sec; exec /usr/bin/sec -detach -pid=/run/sec.pid $${SEC_OPTS} [Install] WantedBy=multi-user.target sec-2.7.8/contrib/itostream.c0000644000175000017500000001374112623126626015213 0ustar ristoristo/* SEC plugin for HP OV ITO/Operations (contributed by Risto Vaarandi). This program reads events from HP OV ITO (now known as HP OV Operations) server or agent event stream and writes them to standard output. Its main task is to act as a link between ITO and an external message processing application (e.g., a correlation engine). The program has been tested with ITO 5.3, 6.0, 7.1, 8.1 and 9.2 This program takes 2 commandline parameters: MSI interface - name used internally by ITO to denote the interface that is used for passing messages to this program. You can pick up arbitrary name, provided that it meets naming convention used by ITO (see ITO Application Integration Guide for more information). Generally, using up to 12 a-z characters as a name should be safe. reopen timeout - if there has been no new data for a given number of seconds, reopen the interface. Using 0 as a value disables reopening. Compiling for Operations 9.2 server on Centos6: gcc -o itostream itostream.c -L/opt/OV/lib64 -lopcsv_r \ -Xlinker '-rpath=/opt/OV/lib64' Compiling for ITO server on Solaris/HP-UX (older versions): gcc -o itostream itostream.c -L/opt/OV/lib -lopcsv -lnsp Compiling for ITO agent: gcc -o itostream itostream.c -DAGENT -L/opt/OV/lib -lopc -lnsp Since ITO agent installation is sometimes broken, you might not have libopc in /opt/OV/lib (which normally is just a symbolic link to libopc_r or some other library in the same directory). In that case you could just try: gcc -o itostream itostream.c -DAGENT -L/opt/OV/lib -lopc_r -lnsp Compiled program needs root-privileges for running. If ITO shared libraries are not found when starting the program, recompile with additional options -Xlinker -rpath /opt/OV/lib */ #include /* stdio stuff */ #include /* free(), atoi() */ #include /* sleep() */ #ifdef AGENT #include "/opt/OV/include/opcapi.h" /* ITO agent-side stuff */ #else #include "/opt/OV/include/opcsvapi.h" /* ITO server-side stuff */ #endif #define ERRORMSGSIZE 1024 #define SLEEPEMPTY 1 /* sleep on empty input before new read attempt */ #define SLEEPOPEN 3 /* sleep between closing and reopening the input */ void error_msg(int code, char *text) { int size; char *ptr; opcdata_get_error_msg(code, &ptr, &size); strncpy(text, ptr, size); text[size] = 0; free(ptr); } int open_if(char *ifname) { int ret; char errortext[ERRORMSGSIZE]; int interface; #ifdef AGENT ret = opcif_open(OPCAGTIF_EXTMSGPROC_READWRITE, ifname, OPCIF_SV_RUNNING | OPCIF_READ_NOWAIT | OPCIF_IGNORE_MSI_ALREADY_EXISTS, 0, &interface); #else ret = opcif_open(OPCSVIF_EXTMSGPROC_READWRITE, ifname, OPCIF_SV_RUNNING | OPCIF_READ_NOWAIT | OPCIF_IGNORE_MSI_ALREADY_EXISTS, 0, &interface); #endif if (ret != OPC_ERR_OK) { error_msg(ret, errortext); fprintf(stderr, "Error opening MSI interface \"%s\": %s\n", ifname, errortext); exit(1); } ret = opcif_register(interface, 0, 0); if (ret != OPC_ERR_OK) { error_msg(ret, errortext); fprintf(stderr, "Error registering condition with MSI interface \"%s\": %s\n", ifname, errortext); opcif_close(interface); exit(1); } return interface; } void find_sev(int severity, char *text) { switch(severity) { case OPC_SEV_UNCHANGED: strcpy(text, "unchanged"); break; case OPC_SEV_UNKNOWN: strcpy(text, "unknown"); break; case OPC_SEV_NORMAL: strcpy(text, "normal"); break; case OPC_SEV_WARNING: strcpy(text, "warning"); break; case OPC_SEV_CRITICAL: strcpy(text, "critical"); break; case OPC_SEV_MINOR: strcpy(text, "minor"); break; case OPC_SEV_MAJOR: strcpy(text, "major"); break; } } int main(int argc, char **argv) { int interface; int reopen, sleepcounter; int ret; char errortext[ERRORMSGSIZE]; opcdata msg; long time; long severity; char sevtext[32]; char *id, *app, *obj, *node; char *msg_group, *msg_text; if (argc < 3) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(1); } /* set stdout buffering to line mode (needed if stdout was redirected to a file or to a pipe) */ setvbuf(stdout, 0, _IOLBF, 0); interface = open_if(argv[1]); reopen = atoi(argv[2]); opcdata_create(OPCDTYPE_EMPTY, &msg); sleepcounter = 0; for (;;) { ret = opcif_read(interface, msg); switch (ret) { case OPC_ERR_OK: sleepcounter = 0; id = opcdata_get_str(msg, OPCDATA_MSGID); time = opcdata_get_long(msg, OPCDATA_CREATION_TIME); severity = opcdata_get_long(msg, OPCDATA_SEVERITY); node = opcdata_get_str(msg, OPCDATA_NODENAME); app = opcdata_get_str(msg, OPCDATA_APPLICATION); obj = opcdata_get_str(msg, OPCDATA_OBJECT); msg_group = opcdata_get_str(msg, OPCDATA_GROUP); msg_text = opcdata_get_str(msg, OPCDATA_MSGTEXT); find_sev(severity, sevtext); printf("id=%s time=%ld sev=%s node=%s app=%s obj=%s msg_grp=%s msg_text=%s\n", id, time, sevtext, node, app, obj, msg_group, msg_text); break; case OPC_ERR_NO_DATA: sleep(SLEEPEMPTY); sleepcounter += SLEEPEMPTY; if (reopen && sleepcounter >= reopen) { fprintf(stderr, "Reopening MSI interface \"%s\"\n", argv[1]); sleepcounter = 0; opcif_close(interface); sleep(SLEEPOPEN); interface = open_if(argv[1]); } break; default: error_msg(ret, errortext); fprintf(stderr, "Error reading from MSI interface \"%s\": %s\n", argv[1], errortext); opcdata_free(&msg); opcif_close(interface); exit(1); } } } sec-2.7.8/contrib/swatch2sec.pl0000755000175000017500000000173712263012625015441 0ustar ristoristo#!/usr/bin/perl -w # swatch2sec (contributed by Johan Nilsson). # Convert a swatch configuration file to a simple event correlator conf. # Far from complete but might save time on long and simple swatch files. # # usage: swatch2sec < ~/.swatchrc > newconf.sec # use strict; while ( my $line = <> ) { chomp $line; # keep comments and whitespace if ( $line =~ /(^#|^\s*$)/ ) { print "$line\n"; next; } # log line if ( $line =~ /\s*watchfor\s*\/(.*)\/$/ ) { print "type=single ptype=regexp pattern=(.*$1.*) desc=Log line action=write - \$1\n\n"; next; # should the pattern ($1) be enough? } # suppress line if ( $line =~ /\s*ignore\s*\/(.*)\/$/ ) { print "type=Suppress\nptype=RegExp\npattern=$1\n\n"; next; } # echo color not supported by sec, try ccze instead if ( $line =~/\s*echo\s*\w/ ){ next; } # not implemented print "## untranslated line from swatch: $line\n"; } sec-2.7.8/contrib/suse.startup0000644000175000017500000000722712263012625015436 0ustar ristoristo#!/bin/sh # # /etc/init.d/sec # and its symbolic link # /(usr/)sbin/rcsec # # LSB compatible service control script; see http://www.linuxbase.org/spec/ # Contributed by Malcolm J Lewis # ### BEGIN INIT INFO # Provides: sec # Required-Start: $syslog $remote_fs # Should-Start: $time ypbind smtp # Required-Stop: $syslog $remote_fs # Should-Stop: $time ypbind smtp # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: sec is an simple event correlator # Description: sec is an simple event correlator ### END INIT INFO SEC_BIN=/usr/bin/sec test -x $SEC_BIN || { echo "$SEC_BIN not installed"; if [ "$1" = "stop" ]; then exit 0; else exit 5; fi; } # Check for existence of needed config file and read it SEC_CONFIG=/etc/sysconfig/sec test -r $SEC_CONFIG || { echo "$SEC_CONFIG not existing"; if [ "$1" = "stop" ]; then exit 0; else exit 6; fi; } # Read config . $SEC_CONFIG . /etc/rc.status # Reset status of this service rc_reset case "$1" in start) echo -n "Starting simple event correlator (sec)" ## Start daemon with startproc(8). If this fails ## the return value is set appropriately by startproc. /sbin/startproc $SEC_BIN $SEC_OPTS # Remember status and be verbose rc_status -v ;; stop) echo -n "Shutting down simple event correlator (sec)" ## Stop daemon with killproc(8) and if this fails ## killproc sets the return value according to LSB. /sbin/killproc -TERM $SEC_BIN # Remember status and be verbose rc_status -v ;; try-restart|condrestart) ## Do a restart only if the service was active before. ## Note: try-restart is now part of LSB (as of 1.9). ## RH has a similar command named condrestart. if test "$1" = "condrestart"; then echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" fi $0 status if test $? = 0; then $0 restart else rc_reset # Not running is not a failure. fi # Remember status and be quiet rc_status ;; restart) ## Stop the service and regardless of whether it was ## running or not, start it again. $0 stop $0 start # Remember status and be quiet rc_status ;; force-reload) ## Signal the daemon to reload its config. Most daemons ## do this on signal 1 (SIGHUP). ## If it does not support it, restart the service if it ## is running. echo -n "Reload service simple event correlator (sec)" ## if it supports it: /sbin/killproc -HUP $SEC_BIN #touch /var/run/jftpgw.pid rc_status -v ## Otherwise: #$0 try-restart #rc_status ;; reload) ## Like force-reload, but if daemon does not support ## signaling, do nothing (!) # If it supports signaling: echo -n "Reload service simple event correlator (sec)" /sbin/killproc -HUP $SEC_BIN #touch /var/run/jftpgw.pid rc_status -v ## Otherwise if it does not support reload: #rc_failed 3 #rc_status -v ;; status) echo -n "Checking for service simple event correlator (sec)" ## Check status with checkproc(8), if process is running ## checkproc will return with exit status 0. # Return value is slightly different for the status command: # 0 - service up and running # 1 - service dead, but /var/run/ pid file exists # 2 - service dead, but /var/lock/ lock file exists # 3 - service not running (unused) # 4 - service status unknown :-( # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) # NOTE: checkproc returns LSB compliant status values. /sbin/checkproc $SEC_BIN # NOTE: rc_status knows that we called this init script with # "status" option and adapts its messages accordingly. rc_status -v ;; *) echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}" exit 1 ;; esac rc_exit sec-2.7.8/contrib/solaris.startcfg0000644000175000017500000000026012263012625016234 0ustar ristoristo# /usr/local/etc/sec/sec.start (config file for SEC) -detach -debug=5 -conf=/usr/local/etc/sec/sec.rules -input=/var/log/syslog.log -log=/var/log/sec.log -pid=/var/run/sec.pid sec-2.7.8/contrib/freebsd.startup0000644000175000017500000000467112263012625016071 0ustar ristoristo#!/bin/sh # # Sample SEC startup script for FreeBSD (contributed by Jo Rhett) # # Add the following lines to /etc/rc.conf to enable sec: # sec_enable (bool): Set to "NO" by default. # Set it to "YES" to enable sec. # # These parameters control the first (or only) instance of sec # sec_flags (str): Set to "" by default. # sec_configfile (str): Set to "/usr/local/etc/sec.cfg" by default. # # To handle multiple instances you can also define # sec_instances="main auth" (list): define the instances (any string) which should be started/stopped # sec_instance_main_flags (str): define the invocation options for the first instance # sec_instance_main_configfile (str): define the config file for the first instance # sec_instance_auth_flags (str): define the invocation options for the second instance # sec_instance_auth_configfile (str): define the config file for the second instance # ...etc # /etc/rc.subr name="sec" rcvar=`set_rcvar` command="/usr/local/bin/sec" command_args="-detach" command_interpreter="/usr/bin/perl" extra_commands="reload" pidfile="/var/run/sec.pid" start_precmd="sec_checkconfig" reload_precmd="sec_checkconfig" restart_precmd="sec_checkconfig" sig_reload=HUP load_rc_config "${name}" [ -z "${sec_enable}" ] && sec_enable="NO" [ -z "${sec_flags}" ] && sec_flags="-log=/var/log/sec.log" [ -z "${sec_configfile}" ] && sec_configfile="/usr/local/etc/sec.conf" [ -z "${sec_instances}" ] && sec_instances="" sec_checkconfig() { if [ -z $instance ] then echo -n "Performing sanity check of sec configuration: " else echo -n "Performing sanity check of sec_${instance} configuration: " fi ${command} -debug=1 -testonly -conf=${sec_configfile} 2>&1 >/dev/null if [ $? != 0 ]; then echo "FAILED" ${command} -testonly -conf=${sec_configfile} return 1 else echo "OK" fi } required_files="${sec_configfile}" sec_flags="-conf=${sec_configfile} -pid=${pidfile} ${sec_flags}" run_rc_command "$1" # Are we handling multiple instances? if [ ! -z "${sec_instances}" ] then for instance in $sec_instances do # Iterate through all instances name="sec_${instance}" pidfile="/var/run/sec_${instance}.pid" eval required_files=\$sec_${instance}_configfile eval sec_${instance}_flags="\"-conf=\$sec_${instance}_configfile -pid=\$pidfile \$sec_${instance}_flags\"" run_rc_command "$1" done fi sec-2.7.8/contrib/convert.pl0000755000175000017500000002400512263012625015044 0ustar ristoristo#!/usr/bin/perl -w # # program for converting SEC 1.1 configuration files to SEC 2.0 format # (contributed by Risto Vaarandi) use Getopt::Long; # read options given in commandline GetOptions( "conf=s" => \$conffile, "separator=s" => \$separator ); if (!defined($conffile)) { print STDERR << "USAGE"; Usage: $0 -conf= [-separator=] USAGE exit(1); } # Default regular expression that is used to detect field boundaries # in configuration file if (!defined($separator)) { $separator = '\s+\|\s+'; } ############################## # Functions ############################## sub log_msg { my($msg) = $_[0]; print STDERR "$msg\n"; } sub convert_actionlist { my($actionlist) = $_[0]; my(@parts, $action, $result); my($context, $lifetime); @parts = split(/\s*;\s*/, $actionlist); $result = ""; foreach $action (@parts) { if ($action =~ /^create\s*(\d*)\s*(.*)/i) { $lifetime = $1; $context = $2; if (!length($context)) { $context = "%s"; } $result .= "create " . $context . " " . $lifetime . "; "; } else { $result .= $action . "; "; } } return $result; } sub convert_config { my($line, $i, $cont, @comp, $type); log_msg("Reading configuration from $conffile..."); if (open(CONFFILE, "$conffile")) { $i = 0; $cont = 0; while () { # check if current line belongs to previous line; # if it does, form a single line from them if ($cont) { $line .= $_; } else { $line = $_; } # remove whitespaces from line beginnings and ends; # if line is empty or all-whitespace, print empty line, # take next line, and set $cont to 0 if ($line =~ /^\s*(.*\S)/) { $line = $1; } else { print "\n"; $cont = 0; next; } # check if line ends with '\'; if it does, remove '\', set $cont # to 1 and jump at the start of loop to read next line, otherwise # set $cont to 0 if (rindex($line, '\\') == length($line) - 1) { chop($line); $cont = 1; next; } else { $cont = 0; } # preserve comment lines if (index($line, '#') == 0) { print $line, "\n"; next; } # split line into fields @comp = split(/$separator/, $line); # find the rule type $type = uc($comp[0]); # ------------------------------------------------------------ # SINGLE rule # ------------------------------------------------------------ if ($type eq "SINGLE") { if (scalar(@comp) < 6 || scalar(@comp) > 7) { log_msg("Wrong number of parameters specified at line $."); next; } $comp[5] = convert_actionlist($comp[5]); print "type=Single\n"; print "continue=$comp[1]\n"; print "ptype=$comp[2]\n"; print "pattern=$comp[3]\n"; if (defined($comp[6])) { print "context=$comp[6]\n"; } print "desc=$comp[4]\n"; print "action=$comp[5]\n"; ++$i; } # ------------------------------------------------------------ # SINGLE_W_SCRIPT rule # ------------------------------------------------------------ elsif ($type eq "SINGLEWITHSCRIPT") { if (scalar(@comp) < 7 || scalar(@comp) > 8) { log_msg("Wrong number of parameters specified at line $."); next; } $comp[6] = convert_actionlist($comp[6]); print "type=SingleWithScript\n"; print "continue=$comp[1]\n"; print "ptype=$comp[2]\n"; print "pattern=$comp[3]\n"; if (defined($comp[7])) { print "context=$comp[7]\n"; } print "script=$comp[4]\n"; print "desc=$comp[5]\n"; print "action=$comp[6]\n"; ++$i; } # ------------------------------------------------------------ # SINGLE_W_SUPPRESS rule # ------------------------------------------------------------ elsif ($type eq "SINGLEWITHSUPPRESS") { if (scalar(@comp) < 7 || scalar(@comp) > 8) { log_msg("Wrong number of parameters specified at line $."); next; } $comp[5] = convert_actionlist($comp[5]); print "type=SingleWithSuppress\n"; print "continue=$comp[1]\n"; print "ptype=$comp[2]\n"; print "pattern=$comp[3]\n"; if (defined($comp[7])) { print "context=$comp[7]\n"; } print "desc=$comp[4]\n"; print "action=$comp[5]\n"; print "window=$comp[6]\n"; ++$i; } # ------------------------------------------------------------ # PAIR rule # ------------------------------------------------------------ elsif ($type eq "PAIR") { if (scalar(@comp) < 11 || scalar(@comp) > 12) { log_msg("Wrong number of parameters specified at line $."); next; } $comp[5] = convert_actionlist($comp[5]); $comp[9] = convert_actionlist($comp[9]); print "type=Pair\n"; print "continue=$comp[1]\n"; print "ptype=$comp[2]\n"; print "pattern=$comp[3]\n"; if (defined($comp[11])) { print "context=$comp[11]\n"; } print "desc=$comp[4]\n"; print "action=$comp[5]\n"; print "continue2=$comp[1]\n"; print "ptype2=$comp[6]\n"; print "pattern2=$comp[7]\n"; if (defined($comp[11])) { $comp[11] =~ s/\$(\d+)/%$1/g; print "context2=$comp[11]\n"; } print "desc2=$comp[8]\n"; print "action2=$comp[9]\n"; print "window=$comp[10]\n"; ++$i; } # ------------------------------------------------------------ # PAIR_W_WINDOW rule # ------------------------------------------------------------ elsif ($type eq "PAIRWITHWINDOW") { if (scalar(@comp) < 11 || scalar(@comp) > 12) { log_msg("Wrong number of parameters specified at line $."); next; } $comp[5] = convert_actionlist($comp[5]); $comp[9] = convert_actionlist($comp[9]); print "type=PairWithWindow\n"; print "continue=$comp[1]\n"; print "ptype=$comp[2]\n"; print "pattern=$comp[3]\n"; if (defined($comp[11])) { print "context=$comp[11]\n"; } print "desc=$comp[4]\n"; print "action=$comp[5]\n"; print "continue2=$comp[1]\n"; print "ptype2=$comp[6]\n"; print "pattern2=$comp[7]\n"; if (defined($comp[11])) { $comp[11] =~ s/\$(\d+)/%$1/g; print "context2=$comp[11]\n"; } print "desc2=$comp[8]\n"; print "action2=$comp[9]\n"; print "window=$comp[10]\n"; ++$i; } # ------------------------------------------------------------ # SINGLE_W_THRESHOLD rule # ------------------------------------------------------------ elsif ($type eq "SINGLEWITHTHRESHOLD") { if (scalar(@comp) < 8 || scalar(@comp) > 9) { log_msg("Wrong number of parameters specified at line $."); next; } $comp[5] = convert_actionlist($comp[5]); print "type=SingleWithThreshold\n"; print "continue=$comp[1]\n"; print "ptype=$comp[2]\n"; print "pattern=$comp[3]\n"; if (defined($comp[8])) { print "context=$comp[8]\n"; } print "desc=$comp[4]\n"; print "action=$comp[5]\n"; print "window=$comp[6]\n"; print "thresh=$comp[7]\n"; ++$i; } # ------------------------------------------------------------ # SINGLE_W_2_THRESHOLDS rule # ------------------------------------------------------------ elsif ($type eq "SINGLEWITH2THRESHOLDS") { if (scalar(@comp) < 12 || scalar(@comp) > 13) { log_msg("Wrong number of parameters specified at line $."); next; } $comp[5] = convert_actionlist($comp[5]); $comp[9] = convert_actionlist($comp[9]); print "type=SingleWith2Thresholds\n"; print "continue=$comp[1]\n"; print "ptype=$comp[2]\n"; print "pattern=$comp[3]\n"; if (defined($comp[12])) { print "context=$comp[12]\n"; } print "desc=$comp[4]\n"; print "action=$comp[5]\n"; print "window=$comp[6]\n"; print "thresh=$comp[7]\n"; print "desc2=$comp[8]\n"; print "action2=$comp[9]\n"; print "window2=$comp[10]\n"; print "thresh2=$comp[11]\n"; ++$i; } # ------------------------------------------------------------ # SUPPRESS rule # ------------------------------------------------------------ elsif ($type eq "SUPPRESS") { if (scalar(@comp) < 3 || scalar(@comp) > 4) { log_msg("Wrong number of parameters specified at line $."); next; } print "type=Suppress\n"; print "ptype=$comp[1]\n"; print "pattern=$comp[2]\n"; if (defined($comp[3])) { print "context=$comp[3]\n"; } ++$i; } # ------------------------------------------------------------ # CALENDAR rule # ------------------------------------------------------------ elsif ($type eq "CALENDAR") { if (scalar(@comp) < 4 || scalar(@comp) > 5) { log_msg("Wrong number of parameters specified at line $."); next; } $comp[3] = convert_actionlist($comp[3]); print "type=Calendar\n"; print "time=$comp[1]\n"; if (defined($comp[4])) { print "context=$comp[4]\n"; } print "desc=$comp[2]\n"; print "action=$comp[3]\n"; ++$i; } # ------------------------------------------------------------ # unknown rule # ------------------------------------------------------------ else { log_msg("Unknown rule type '$type' specified at line $."); } print "\n"; } close CONFFILE; log_msg("$i rules converted"); } else { log_msg("Can't open configuration file $conffile, exiting"); } } convert_config(); sec-2.7.8/contrib/solaris.startup0000644000175000017500000000476612263012625016140 0ustar ristoristo#!/bin/bash # # /etc/rc3.d/S98sec # # Modified start up script for Solaris (contributed by Jason Chambers) # Added the different SIGs to script STARTCFG=/usr/local/etc/sec/sec.start SEC=/usr/local/bin/sec SECPID=/var/run/sec.pid RETVAL=0 start() { echo -n "Starting up Syslog Event Correlator: " while read command do command=`echo $command | sed -e "s/\#.*//" -e "s/^ *//" -e 's/ *$//' -e '/^$/d'` if [ ! -z "$command" ] ; then $SEC $command RETVAL=$(( $? + RETVAL )) fi done < $STARTCFG if [ $RETVAL -eq 0 ] ; then echo else echo "Failure starting SEC" echo fi } stop() { echo -n "Shutting down sec: " # SIGTERM /bin/kill -15 `/bin/cat $SECPID` RETVAL=$? if [ $RETVAL -eq 0 ] ; then echo else echo "Failed to shutdown SEC" echo fi } killme() { echo -n "restart SEC completely, all variables and contexts will be deleted" # SIGHUP /bin/kill -1 `/bin/cat $SECPID` RETVAL=$? if [ $RETVAL -eq 0 ] ; then echo else echo "Failed to reloading SEC" echo fi } reload() { echo -n "Re-reading config file, all variables and contexts will not be deleted" # SIGABRT /bin/kill -6 `/bin/cat $SECPID` RETVAL=$? if [ $RETVAL -eq 0 ] ; then echo else echo "Failed to reloading SEC" echo fi } dump() { echo -n "Dumping stats to $DUMPFILE" # SIGUSR1 /bin/kill -16 `/bin/cat $SECPID` RETVAL=$? if [ $RETVAL -eq 0 ] ; then echo else echo "Failed dumping stats" echo fi } logrotate() { echo -n "re-opening my logfile" # SIGUSR2 /bin/kill -17 `/bin/cat $SECPID` RETVAL=$? if [ $RETVAL -eq 0 ] ; then echo else echo "Failed re-opening log file" echo fi } # See how we were called. case "$1" in start) start ;; stop) stop ;; killme) killme ;; reload) reload ;; dump) dump ;; logrotate) logrotate ;; *) echo "Usage: sec {start|stop|reload|dump|logrotate}" exit 1 esac exit $RETVAL sec-2.7.8/sec0000755000175000017500000130033112623126723012071 0ustar ristoristo#!/usr/bin/perl -w # # SEC (Simple Event Correlator) 2.7.8 - sec # Copyright (C) 2000-2015 Risto Vaarandi # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # package main::SEC; # Parameters: par1 - perl code to be evaluated # par2 - if set to 0, the code will be evaluated in scalar # context; if 1, list context is used for evaluation # Action: calls eval() for the perl code par1, and returns an array with # the eval() return value(s). The first element of the array # indicates whether the code was evaluated successfully (i.e., # the compilation didn't fail). If code evaluation fails, the # first element of the return array contains the error string. sub call_eval { my($code, $listcontext) = @_; my($ok, @result); $ok = 1; if ($listcontext) { @result = eval $code; } else { $result[0] = eval $code; } if ($@) { $ok = 0; chomp($result[0] = $@); } return ($ok, @result); } ###################################################################### package main; use strict; ##### List of global variables ##### use vars qw( @actioncopyfunc @actionsubstfunc $blocksize $bufpos $bufsize @calendar %cfset2cfile $check_timeout %children $childterm $cleantime @conffilepat @conffiles %config_ltimes %config_mtimes %config_options %configuration %context_list %corr_list $debuglevel $debuglevelinc $detach $dumpdata $dumpfile $dumpfts @events %event_buffer $evstoresize @execactionfunc $fromstart $help @inputfilepat @inputfiles %inputsrc @input_buffer %input_buffers @input_sources $input_timeout $intcontexts $intevents $int_context $jointbuf $keepopen $lastcleanuptime $lastconfigload $logfile $logopen @maincfiles @matchfunc @matchrulefunc $openlog %output_files %output_tcpconn %output_tcpsock %output_udgram %output_udpsock %output_ustream @pending_events $pidfile %pmatch_cache $poll_timeout $processedlines @processrulefunc $quoting $rcfile_status @readbuffer $refresh $reopen_timeout $rwfifo $SEC_COPYRIGHT $SEC_LICENSE $SEC_USAGE $SEC_VERSION $SYSLOGAVAIL $sec_options $sigreceived $socket_timeout $softrefresh $startuptime $syslogf $syslogopen $tail $terminate $testonly $timeout_script %variables $version $WIN32 ); ##### Load modules and set some global variables ##### use POSIX qw(:errno_h :sys_wait_h SEEK_SET SEEK_CUR SEEK_END setsid ctermid getpgrp tcgetpgrp); use Getopt::Long; use Fcntl; use Socket; use IO::Handle; # check if Sys::Syslog is available $SYSLOGAVAIL = eval { require Sys::Syslog }; # check if the platform is win32 $WIN32 = ($^O =~ /win/i && $^O !~ /cygwin/i && $^O !~ /darwin/i); # set version and usage variables $SEC_VERSION = "SEC (Simple Event Correlator) 2.7.8"; $SEC_COPYRIGHT = "Copyright (C) 2000-2015 Risto Vaarandi"; $SEC_USAGE = qq!Usage: $0 [options] Options: --conf= ... --input=[=] ... --input-timeout= --timeout-script= --reopen-timeout= --check-timeout= --poll-timeout= --socket-timeout= --blocksize= --bufsize= --evstoresize= --cleantime= --log= --syslog= --debug= --pid= --dump= --dumpfts, --nodumpfts --quoting, --noquoting --tail, --notail --fromstart, --nofromstart --detach, --nodetach --jointbuf, --nojointbuf --keepopen, --nokeepopen --rwfifo, --norwfifo --childterm, --nochildterm --intevents, --nointevents --intcontexts, --nointcontexts --testonly, --notestonly --help, -? --version !; $SEC_LICENSE = q! 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. !; ##### List of internal constants ##### use constant INVALIDVALUE => -1; use constant SINGLE => 0; use constant SINGLE_W_SUPPRESS => 1; use constant SINGLE_W_SCRIPT => 2; use constant PAIR => 3; use constant PAIR_W_WINDOW => 4; use constant SINGLE_W_THRESHOLD => 5; use constant SINGLE_W_2_THRESHOLDS => 6; use constant EVENT_GROUP => 7; use constant SUPPRESS => 8; use constant CALENDAR => 9; use constant JUMP => 10; use constant SUBSTR => 0; use constant REGEXP => 1; use constant PERLFUNC => 2; use constant CACHED => 3; use constant NSUBSTR => 4; use constant NREGEXP => 5; use constant NPERLFUNC => 6; use constant NCACHED => 7; use constant TVALUE => 8; use constant DONTCONT => 0; use constant TAKENEXT => 1; use constant GOTO => 2; use constant ENDMATCH => 3; use constant NONE => 0; use constant LOGONLY => 1; use constant WRITE => 2; use constant WRITEN => 3; use constant CLOSEF => 4; use constant OWRITECL => 5; use constant UDGRAM => 6; use constant CLOSEUDGR => 7; use constant USTREAM => 8; use constant CLOSEUSTR => 9; use constant UDPSOCK => 10; use constant CLOSEUDP => 11; use constant TCPSOCK => 12; use constant CLOSETCP => 13; use constant SHELLCOMMAND => 14; use constant SPAWN => 15; use constant CSPAWN => 16; use constant PIPE => 17; use constant CREATECONTEXT => 18; use constant DELETECONTEXT => 19; use constant OBSOLETECONTEXT => 20; use constant SETCONTEXT => 21; use constant ALIAS => 22; use constant UNALIAS => 23; use constant ADD => 24; use constant PREPEND => 25; use constant FILL => 26; use constant REPORT => 27; use constant COPYCONTEXT => 28; use constant EMPTYCONTEXT => 29; use constant POP => 30; use constant SHIFT => 31; use constant EXISTS => 32; use constant GETSIZE => 33; use constant GETALIASES => 34; use constant GETLIFETIME => 35; use constant GETCTIME => 36; use constant SETCTIME => 37; use constant EVENT => 38; use constant TEVENT => 39; use constant CEVENT => 40; use constant RESET => 41; use constant GETWINPOS => 42; use constant SETWINPOS => 43; use constant ASSIGN => 44; use constant ASSIGNSQ => 45; use constant FREE => 46; use constant EVAL => 47; use constant CALL => 48; use constant LCALL => 49; use constant REWRITE => 50; use constant IF => 100; use constant WHILE => 101; use constant BREAK => 102; use constant CONTINUE => 103; use constant OPERAND => 0; use constant NEGATION => 1; use constant AND => 2; use constant OR => 3; use constant EXPRESSION => 4; use constant ECODE => 5; use constant CCODE => 6; use constant CCODE2 => 7; use constant VARSET => 8; use constant EXPRSYMBOL => "\0"; use constant LOG_WITHOUT_LEVEL => 0; use constant LOG_CRIT => 1; use constant LOG_ERR => 2; use constant LOG_WARN => 3; use constant LOG_NOTICE => 4; use constant LOG_INFO => 5; use constant LOG_DEBUG => 6; use constant SYSLOG_LEVELS => { 0 => "notice", 1 => "crit", 2 => "err", 3 => "warning", 4 => "notice", 5 => "info", 6 => "debug" }; use constant SEPARATOR => " | "; use constant TERMTIMEOUT => 3; use constant BATCHREADLIMIT => 8192; use constant SECEVENT_INT_CONTEXT => "SEC_INTERNAL_EVENT"; use constant SYNEVENT_INT_CONTEXT => "_INTERNAL_EVENT"; use constant FILEVENT_INT_CONTEXT_PREF => "_FILE_EVENT_"; ############################################################### # ------------------------- FUNCTIONS ------------------------- ############################################################### ############################## # Functions related to logging ############################## # Parameters: par1 - name of the logfile # Action: logfile will be opened. Filehandle of the logfile will be # saved to the global filehandle LOGFILE. sub open_logfile { my($logfile) = $_[0]; if (open(LOGFILE, ">>$logfile")) { select LOGFILE; $| = 1; select STDOUT; $logopen = 1; } else { if (-t STDERR || -f STDERR) { print STDERR "Can't open logfile $logfile ($!)\n"; } $logopen = 0; } } # Parameters: par1 - syslog facility # Action: open connection to the system logger with the facility par1. sub open_syslog { my($facility) = $_[0]; my($progname); if (!$SYSLOGAVAIL) { if (-t STDERR || -f STDERR) { print STDERR "Can't connect to syslog (no Sys::Syslog)\n"; } $syslogopen = 0; return; } $progname = $0; $progname =~ s/.*\///; eval { Sys::Syslog::openlog($progname, "pid", $facility) }; if ($@) { if (-t STDERR || -f STDERR) { print STDERR "Can't connect to syslog ($@)\n"; } $syslogopen = 0; return; } $syslogopen = 1; } # Parameters: par1 - severity of the log message # par2, par3, ... - strings to be logged # Action: if par1 is smaller or equal to the current logging level (i.e., # the message must be logged), then strings par2, par3, ... # will be equipped with timestamp and written to LOGFILE and/or # forwarded to the system logger as a single line. If STDERR is # connected to terminal, message will also be written there. sub log_msg { my($level) = shift(@_); my($ltime, $msg); if ($debuglevel < $level) { return; } if (!$logopen && !$syslogopen && ! -t STDERR) { return; } $msg = join(" ", @_); if (-t STDERR) { print STDERR "$msg\n"; } if ($logopen) { $ltime = localtime(time()); print LOGFILE "$ltime: $msg\n"; } # if call to syslog() fails (e.g., because syslog daemon is going through # restart), older versions of Sys::Syslog will die, thus we use eval if ($syslogopen) { eval { Sys::Syslog::syslog(SYSLOG_LEVELS->{$level}, $msg) }; } } ####################################################### # Functions related to configuration file(s) processing ####################################################### # Parameters: par1, par2, .. - strings # Action: All 2-byte substrings in par1, par2, .. that denote special # symbols ("\n", "\t", ..) will be replaced with corresponding # special symbols sub subst_specchar { my(%specchar, $string); $specchar{"0"} = ""; $specchar{"n"} = "\n"; $specchar{"r"} = "\r"; $specchar{"s"} = " "; $specchar{"t"} = "\t"; $specchar{"\\"} = "\\"; foreach $string (@_) { $string =~ s/\\(0|n|r|s|t|\\)/$specchar{$1}/g; } } # Parameters: par1 - string that is checked for match variables # par2, .. - one or more tokens that match variables begin with # Action: return 1 if the string par1 contains match variables, 0 otherwise sub contains_matchvars { my($string) = shift @_; my($token, $string2, %subst); # invoke subst_string() function for the input string and empty match # value hash - if the string contains match variables, they are replaced # with empty strings, and the result is different from the original string foreach $token (@_) { $string2 = $string; subst_string(\%subst, $string2, $token); if ($string ne $string2) { return 1; } } return 0; } # Parameters: par1 - reference to a context expression # par2, .. - one or more tokens that match variables begin with # Action: return 1 if expression par1 contains match variables, 0 otherwise sub volatile_context { my($ref) = shift @_; my($i, $j, $elem); $i = 0; $j = scalar(@{$ref}); while ($i < $j) { if ($ref->[$i] == OPERAND || $ref->[$i] == ECODE || $ref->[$i] == VARSET) { if (contains_matchvars($ref->[$i+1], @_)) { return 1; } $i += 2; } elsif ($ref->[$i] == EXPRESSION) { if (volatile_context($ref->[$i+1], @_)) { return 1; } $i += 2; } elsif ($ref->[$i] == CCODE || $ref->[$i] == CCODE2) { foreach $elem (@{$ref->[$i+1]}) { if (contains_matchvars($elem, @_)) { return 1; } } $i += 3; } else { ++$i; } } return 0; } # Parameters: par1 - expression # par2 - reference to an array # Action: parentheses and their contents will be replaced with special # symbols EXPRSYMBOL in par 1. The expressions inside parentheses # will be returned in par2. Previous content of the array par2 # is erased. If par1 was parsed successfully, the modified par1 # will be returned, otherwise undef is returned. sub replace_subexpr { my($expression, $expr_ref) = @_; my($i, $j, $l, $pos); my($char, $prev); @{$expr_ref} = (); $i = 0; $j = 0; $l = length($expression); $pos = undef; $prev = ""; while ($i < $l) { # process expression par1 from the start and inspect every symbol, # adding 1 to $j for every '(' and subtracting 1 for every ')'; # if a parenthesis is masked with a backslash, it is ignored $char = substr($expression, $i, 1); if ($prev ne "\\") { if ($char eq "(") { ++$j; } elsif ($char eq ")") { --$j; } } # After observing first '(' save its position to $pos; # after observing its counterpart ')' replace everything # from '(' to ')' with EXPRSYMBOL (including possible nested # expressions), and save the content of parentheses; # if at some point $j becomes negative, the parentheses must # be unbalanced if ($j == 1 && !defined($pos)) { $pos = $i; } elsif ($j == 0 && defined($pos)) { # take symbols starting from position $pos+1 (next symbol after # '(') up to position $i-1 (the symbol before ')'), and save # the symbols to array push @{$expr_ref}, substr($expression, $pos + 1, $i - $pos - 1); # replace both the parentheses and the symbols between them # with EXPRSYMBOL substr($expression, $pos, $i - $pos + 1) = EXPRSYMBOL; # set the variables according to changes in expression $i = $pos; $l = length($expression); $pos = undef; $char = ""; } elsif ($j < 0) { return undef; } # extra ')' was found $prev = $char; ++$i; } # if the parsing ended with non-zero $j, the parentheses were unbalanced if ($j == 0) { return $expression; } else { return undef; } } # Parameters: par1 - continue value (string) # par2 - the name of the configuration file # par3 - line number in configuration file # Action: par1 will be analyzed and the integer continue value with # an optional jump label will be returned. # If errors are found when analyzing par1, error message # about improper line par3 in configuration file will be logged. sub analyze_continue { my($continue, $conffile, $lineno) = @_; if (uc($continue) eq "TAKENEXT") { return (TAKENEXT, undef); } elsif (uc($continue) eq "DONTCONT") { return (DONTCONT, undef); } elsif (uc($continue) eq "ENDMATCH") { return (ENDMATCH, undef); } elsif ($continue =~ /^goto\s+(.*\S)/i) { return (GOTO, $1); } log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid continue value '$continue'"); return INVALIDVALUE; } # Parameters: par1 - pattern type (string) # par2 - pattern # par3 - the name of the configuration file # par4 - line number in configuration file # par5 - if we are dealing with the second pattern of Pair* # rule, par5 contains the type of the first pattern # Action: par1 and par2 will be analyzed and tuple of integers # (pattern type, line count, compiled pattern) will be returned # (line count shows how many lines the pattern is designed to match). # If pattern is a second regular expression pattern of Pair rule which # contains match variables, the expression will not be compiled and # a corresponding flag is added to the return list. # If errors are found when analyzing par1 and par2, error message # about improper line par4 in configuration file will be logged. sub analyze_pattern { my($pattype, $pat, $conffile, $lineno, $fptype) = @_; my($negate, $lines, $pat2, $ncomp); my($evalok, $retval); if ($pattype =~ /^(n?)regexp(?:0*([^\D0]\d*))?$/i) { if (length($1)) { $negate = 1; } else { $negate = 0; } if (defined($2)) { $lines = $2; } else { $lines = 1; } if ($bufsize && $lines > $bufsize) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Pattern type '$pattype' is designed to match $lines lines,", "please set --bufsize command line option to at least $lines"); return (INVALIDVALUE, INVALIDVALUE, INVALIDVALUE); } if (!defined($fptype) || $fptype == TVALUE || $fptype == SUBSTR || $fptype == NSUBSTR || !contains_matchvars($pat, '$')) { $pat2 = eval { qr/$pat/ }; if ($@) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid regular expression '$pat':", $@); return (INVALIDVALUE, INVALIDVALUE, INVALIDVALUE); } } else { $pat2 = $pat; $ncomp = 1; } if ($negate) { return (NREGEXP, $lines, $pat2, $ncomp); } else { return (REGEXP, $lines, $pat2, $ncomp); } } elsif ($pattype =~ /^(n?)substr(?:0*([^\D0]\d*))?$/i) { if (length($1)) { $negate = 1; } else { $negate = 0; } if (defined($2)) { $lines = $2; } else { $lines = 1; } if ($bufsize && $lines > $bufsize) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Pattern type '$pattype' is designed to match $lines lines,", "please set --bufsize command line option to at least $lines"); return (INVALIDVALUE, INVALIDVALUE, INVALIDVALUE); } subst_specchar($pat); if ($negate) { return (NSUBSTR, $lines, $pat); } else { return (SUBSTR, $lines, $pat); } } elsif ($pattype =~ /^(n?)perlfunc(?:0*([^\D0]\d*))?$/i) { if (length($1)) { $negate = 1; } else { $negate = 0; } if (defined($2)) { $lines = $2; } else { $lines = 1; } if ($bufsize && $lines > $bufsize) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Pattern type '$pattype' is designed to match $lines lines,", "please set --bufsize command line option to at least $lines"); return (INVALIDVALUE, INVALIDVALUE, INVALIDVALUE); } ($evalok, $retval) = SEC::call_eval($pat, 0); if (!$evalok || !defined($retval) || ref($retval) ne "CODE") { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid function '$pat', eval didn't return a code reference:", defined($retval)?"$retval":"undef"); return (INVALIDVALUE, INVALIDVALUE, INVALIDVALUE); } if ($negate) { return (NPERLFUNC, $lines, $retval); } else { return (PERLFUNC, $lines, $retval); } } elsif ($pattype =~ /^(n?)cached$/i) { if (length($1)) { $negate = 1; } else { $negate = 0; } if ($pat !~ /^[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid cached pattern name '$pat':", "the name does not have the form", "[||]..."); return (INVALIDVALUE, INVALIDVALUE, INVALIDVALUE); } if ($negate) { return (NCACHED, 1, $pat); } else { return (CACHED, 1, $pat); } } elsif ($pattype =~ /^tvalue$/i) { if (uc($pat) ne "TRUE" && uc($pat) ne "FALSE") { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid truth value '$pat'"); return (INVALIDVALUE, INVALIDVALUE, INVALIDVALUE); } return (TVALUE, 1, uc($pat) eq "TRUE"); } log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid pattern type '$pattype'"); return (INVALIDVALUE, INVALIDVALUE, INVALIDVALUE); } # Parameters: par1 - pattern type # par2 - variable map (string) # par3 - reference to the variable map hash # par4 - the name of the configuration file # par5 - line number in configuration file # Action: variable map par2 will be analyzed and saved into the hash par3. # If no errors are detected, 1 is returned. Otherwise error message # about improper line par5 in configuration file will be logged, # and 0 is returned. If the pattern type does not assume a variable # map (e.g., TValue), par3 will be set to empty hash, a warning is # logged and 1 is returned. sub analyze_varmap { my($pattype, $varmap, $maphash_ref, $conffile, $lineno) = @_; my(@varmap, $mapping); %{$maphash_ref} = (); if ($pattype != REGEXP && $pattype != PERLFUNC) { log_msg(LOG_WARN, "Rule in $conffile at line $lineno:", "Variable maps are supported for RegExp and PerlFunc patterns only,", "ignoring variable map '$varmap'"); return 1; } @varmap = split(/\s*;\s*/, $varmap); foreach $mapping (@varmap) { if ($mapping =~ /^\s*([[:alpha:]]\w*)(?:\s*=\s*0*(\d+))?\s*$/) { $maphash_ref->{"$1"} = $2; } else { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid variable map '$varmap':", "the variable mapping '$mapping' does not have the form", "[||]... [= ]"); return 0; } } return 1; } # Parameters: par1, par2, .. - strings # Action: for each string, remove the outer pair of parens and backslashes # from the front of other parens sub process_action_parens { my($string); foreach $string (@_) { if ($string =~ /^\s*\(\s*(.*?)\s*\)\s*$/) { $string = $1; } $string =~ s/\\([()])/$1/g; } } # Parameters: par1 - action # par2 - the name of the configuration file # par3 - line number in configuration file # par4 - rule ID # par5 - action with masked subexpressions # par6 - list of subexpressions # Action: par1 will be analyzed and pair of integers # (action type, action description) will be returned. If errors # are found when analyzing par1, error message about improper # line par3 in configuration file will be logged. sub analyze_action { my($action, $conffile, $lineno, $ruleid, $action2, $list) = @_; my($keyword, $file, $peer, $cmdline); my($sign, $rule, $count); my($actionlist, @action); my($actionlist2, @action2); my($createafter, $event, $timestamp); my($lifetime, $context, $alias); my($variable, $value, $code, $codeptr, $params, $evalok); if ($action =~ /^none$/i) { return NONE; } elsif ($action =~ /^logonly(?:\s+(.*))?$/i) { $event = defined($1)?$1:""; process_action_parens($event); if (!length($event)) { $event = "%s"; } return (LOGONLY, $event); } elsif ($action =~ /^(write|writen|owritecl|udgram|ustream)\s+(\S+)\s*(.*)/i) { $keyword = lc($1); $file = $2; $event = $3; if ($WIN32 && ($keyword eq "udgram" || $keyword eq "ustream")) { log_msg(LOG_ERR, "$keyword action is not supported on Win32"); return INVALIDVALUE; } process_action_parens($file, $event); if (!length($file)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty filename given for $keyword action"); return INVALIDVALUE; } if (!length($event)) { $event = "%s"; } if ($keyword eq "write") { return (WRITE, $file, $event); } elsif ($keyword eq "writen") { return (WRITEN, $file, $event); } elsif ($keyword eq "owritecl") { return (OWRITECL, $file, $event); } elsif ($keyword eq "udgram") { return (UDGRAM, $file, $event); } else { return (USTREAM, $file, $event); } } elsif ($action =~ /^(closef|closeudgr|closeustr)\s+(\S+)$/i) { $keyword = lc($1); $file = $2; if ($WIN32 && ($keyword eq "closeudgr" || $keyword eq "closeustr")) { log_msg(LOG_ERR, "$keyword action is not supported on Win32"); return INVALIDVALUE; } process_action_parens($file); if (!length($file)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty filename given for $keyword action"); return INVALIDVALUE; } if ($keyword eq "closef") { return (CLOSEF, $file); } elsif ($keyword eq "closeudgr") { return (CLOSEUDGR, $file); } else { return (CLOSEUSTR, $file); } } elsif ($action =~ /^(udpsock|tcpsock)\s+(\S+)\s*(.*)/i) { $keyword = lc($1); $peer = $2; $event = $3; process_action_parens($peer, $event); if (!length($peer)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty peername given for $keyword action"); return INVALIDVALUE; } if (!length($event)) { $event = "%s"; } if ($keyword eq "udpsock") { return (UDPSOCK, $peer, $event); } else { return (TCPSOCK, $peer, $event); } } elsif ($action =~ /^(closeudp|closetcp)\s+(\S+)$/i) { $keyword = lc($1); $peer = $2; process_action_parens($peer); if (!length($peer)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty peername given for $keyword action"); return INVALIDVALUE; } if ($keyword eq "closeudp") { return (CLOSEUDP, $peer); } else { return (CLOSETCP, $peer); } } elsif ($action =~ /^shellcmd\s+(.*\S)/i) { $cmdline = $1; process_action_parens($cmdline); if (!length($cmdline)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty commandline given for shellcmd action"); return INVALIDVALUE; } return (SHELLCOMMAND, $cmdline); } elsif ($action =~ /^spawn\s+(.*\S)/i) { if ($WIN32) { log_msg(LOG_ERR, "spawn action is not supported on Win32"); return INVALIDVALUE; } $cmdline = $1; process_action_parens($cmdline); if (!length($cmdline)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty commandline given for spawn action"); return INVALIDVALUE; } return (SPAWN, $cmdline); } elsif ($action =~ /^cspawn\s+(\S+)\s+(.*\S)/i) { if ($WIN32) { log_msg(LOG_ERR, "cspawn action is not supported on Win32"); return INVALIDVALUE; } $context = $1; $cmdline = $2; process_action_parens($context, $cmdline); if (!length($context)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty context name given for cspawn action"); return INVALIDVALUE; } if (!length($cmdline)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty commandline given for cspawn action"); return INVALIDVALUE; } return (CSPAWN, $context, $cmdline); } elsif ($action =~ /^pipe\s+'([^']*)'(?:\s+(.*))?$/i) { $event = $1; $cmdline = defined($2)?$2:""; process_action_parens($event, $cmdline); if (!length($event)) { $event = "%s"; } return (PIPE, $event, $cmdline); } elsif ($action =~ /^create(?:\s+(\S*)\s*(\S*)\s*(.*))?$/i) { $context = defined($1)?$1:""; $lifetime = defined($2)?$2:""; $actionlist = defined($3)?$3:""; process_action_parens($context, $lifetime); # strip outer parentheses from actionlist if they exist if ($actionlist =~ /^\s*\(\s*(.*?)\s*\)\s*$/) { $actionlist = $1; } if (!length($context)) { $context = "%s"; } if (!length($lifetime)) { $lifetime = 0; } if (length($actionlist)) { if (!analyze_actionlist($actionlist, \@action, $conffile, $lineno, $ruleid)) { return INVALIDVALUE; } return (CREATECONTEXT, $context, $lifetime, [ @action ]); } return (CREATECONTEXT, $context, $lifetime, []); } elsif ($action =~ /^(delete|obsolete|unalias)(?:\s+(\S+))?$/i) { $keyword = lc($1); $context = defined($2)?$2:""; process_action_parens($context); if (!length($context)) { $context = "%s"; } if ($keyword eq "delete") { return (DELETECONTEXT, $context); } elsif ($keyword eq "obsolete") { return (OBSOLETECONTEXT, $context); } else { return (UNALIAS, $context); } } elsif ($action =~ /^set\s+(\S+)\s+(\S+)\s*(.*)/i) { $context = $1; $lifetime = $2; $actionlist = $3; process_action_parens($context, $lifetime); # strip outer parentheses from actionlist if they exist if ($actionlist =~ /^\s*\(\s*(.*?)\s*\)\s*$/) { $actionlist = $1; } if (!length($context)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty context name given for set action"); return INVALIDVALUE; } if (!length($lifetime)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty lifetime given for set action"); return INVALIDVALUE; } if (length($actionlist)) { if (!analyze_actionlist($actionlist, \@action, $conffile, $lineno, $ruleid)) { return INVALIDVALUE; } return (SETCONTEXT, $context, $lifetime, [ @action ]); } return (SETCONTEXT, $context, $lifetime, []); } elsif ($action =~ /^alias\s+(\S+)(?:\s+(\S+))?$/i) { $context = $1; $alias = defined($2)?$2:""; process_action_parens($context, $alias); if (!length($context)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty context name given for alias action"); return INVALIDVALUE; } if (!length($alias)) { $alias = "%s"; } return (ALIAS, $context, $alias); } elsif ($action =~ /^(add|prepend|fill)\s+(\S+)\s*(.*)/i) { $keyword = lc($1); $context = $2; $event = $3; process_action_parens($context, $event); if (!length($context)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty context name given for $keyword action"); return INVALIDVALUE; } if (!length($event)) { $event = "%s"; } if ($keyword eq "add") { return (ADD, $context, $event); } elsif ($keyword eq "prepend") { return (PREPEND, $context, $event); } else { return (FILL, $context, $event); } } elsif ($action =~ /^report\s+(\S+)\s*(.*)/i) { $context = $1; $cmdline = $2; process_action_parens($context, $cmdline); if (!length($context)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty context name given for report action"); return INVALIDVALUE; } return (REPORT, $context, $cmdline); } elsif ($action =~ /^(copy|pop|shift)\s+(\S+)\s+(\S+)$/i) { $keyword = lc($1); $context = $2; $variable = $3; process_action_parens($context); if (!length($context)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty context name given for $keyword action"); return INVALIDVALUE; } if ($variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } if ($keyword eq "copy") { return (COPYCONTEXT, $context, substr($variable, 1)); } elsif ($keyword eq "pop") { return (POP, $context, substr($variable, 1)); } else { return (SHIFT, $context, substr($variable, 1)); } } elsif ($action =~ /^empty\s+(\S+)(?:\s+(\S+))?$/i) { $context = $1; $variable = defined($2)?$2:""; process_action_parens($context); if (!length($context)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty context name given for empty action"); return INVALIDVALUE; } if (length($variable) && $variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } if (!length($variable)) { return (EMPTYCONTEXT, $context, ""); } return (EMPTYCONTEXT, $context, substr($variable, 1)); } elsif ($action =~ /^(exists|getsize|getaliases|getltime|getctime)\s+(\S+)\s+(\S+)$/i) { $keyword = lc($1); $variable = $2; $context = $3; process_action_parens($context); if ($variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } if (!length($context)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty context name given for $keyword action"); return INVALIDVALUE; } if ($keyword eq "exists") { return (EXISTS, substr($variable, 1), $context); } elsif ($keyword eq "getsize") { return (GETSIZE, substr($variable, 1), $context); } elsif ($keyword eq "getaliases") { return (GETALIASES, substr($variable, 1), $context); } elsif ($keyword eq "getltime") { return (GETLIFETIME, substr($variable, 1), $context); } else { return (GETCTIME, substr($variable, 1), $context); } } elsif ($action =~ /^setctime\s+(\S+)\s+(\S+)$/i) { $timestamp = $1; $context = $2; process_action_parens($context); if (!length($timestamp)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty timestamp given for setwpos action"); return INVALIDVALUE; } if (!length($context)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty context name given for size action"); return INVALIDVALUE; } return (SETCTIME, $timestamp, $context); } elsif ($action =~ /^event(?:\s+0*(\d+))?(?:\s+(.*))?$/i) { $createafter = defined($1)?$1:""; $event = defined($2)?$2:""; process_action_parens($event); if (!length($createafter)) { $createafter = 0; } if (!length($event)) { $event = "%s"; } return (EVENT, $createafter, $event); } elsif ($action =~ /^tevent\s+(\S+)\s*(.*)/i) { $createafter = $1; $event = $2; process_action_parens($createafter, $event); if (!length($createafter)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty time offset given for tevent action"); return INVALIDVALUE; } if (!length($event)) { $event = "%s"; } return (TEVENT, $createafter, $event); } elsif ($action =~ /^cevent\s+(\S+)\s+(\S+)\s*(.*)/i) { $context = $1; $createafter = $2; $event = $3; process_action_parens($context, $createafter, $event); if (!length($context)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty context name given for cevent action"); return INVALIDVALUE; } if (!length($createafter)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty time offset given for cevent action"); return INVALIDVALUE; } if (!length($event)) { $event = "%s"; } return (CEVENT, $context, $createafter, $event); } elsif ($action =~ /^reset(?:\s+([+-]?)0*(\d+))?(?:\s+(.*))?$/i) { $sign = defined($1)?$1:""; $rule = defined($2)?$2:""; $event = defined($3)?$3:""; process_action_parens($event); if (length($rule)) { if ($sign eq "+") { $rule = $ruleid + $rule; } elsif ($sign eq "-") { $rule = $ruleid - $rule; } elsif (!$rule) { $rule = $ruleid; } else { --$rule; } } else { $rule = ""; } if (!length($event)) { $event = "%s"; } return (RESET, $conffile, $rule, $event); } elsif ($action =~ /^getwpos\s+(\S+)\s+([+-]?)0*(\d+)(?:\s+(.*))?$/i) { $variable = $1; $sign = $2; $rule = $3; $event = defined($4)?$4:""; process_action_parens($event); if ($variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } if ($sign eq "+") { $rule = $ruleid + $rule; } elsif ($sign eq "-") { $rule = $ruleid - $rule; } elsif (!$rule) { $rule = $ruleid; } else { --$rule; } if (!length($event)) { $event = "%s"; } return (GETWINPOS, substr($variable, 1), $conffile, $rule, $event); } elsif ($action =~ /^setwpos\s+(\S+)\s+([+-]?)0*(\d+)(?:\s+(.*))?$/i) { $timestamp = $1; $sign = $2; $rule = $3; $event = defined($4)?$4:""; process_action_parens($timestamp, $event); if ($sign eq "+") { $rule = $ruleid + $rule; } elsif ($sign eq "-") { $rule = $ruleid - $rule; } elsif (!$rule) { $rule = $ruleid; } else { --$rule; } if (!length($timestamp)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty timestamp given for setwpos action"); return INVALIDVALUE; } if (!length($event)) { $event = "%s"; } return (SETWINPOS, $timestamp, $conffile, $rule, $event); } elsif ($action =~ /^(assign|assignsq)\s+(\S+)\s*(.*)/i) { $keyword = lc($1); $variable = $2; $value = $3; process_action_parens($value); if ($variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } if (!length($value)) { $value = "%s"; } if ($keyword eq "assign") { return (ASSIGN, substr($variable, 1), $value); } else { return (ASSIGNSQ, substr($variable, 1), $value); } } elsif ($action =~ /^free\s+(\S+)$/i) { $variable = $1; if ($variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } return (FREE, substr($variable, 1)); } elsif ($action =~ /^eval\s+(\S+)\s+(.*\S)/i) { $variable = $1; $code = $2; process_action_parens($code); if ($variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } return (EVAL, substr($variable, 1), $code); } elsif ($action =~ /^call\s+(\S+)\s+(\S+)\s*(.*)/i) { $variable = $1; $codeptr = $2; $params = $3; process_action_parens($params); if ($variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } if ($codeptr !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $codeptr does not have the form", "%[||]..."); return INVALIDVALUE; } return (CALL, substr($variable, 1), substr($codeptr, 1), [ split(' ', $params) ]); } elsif ($action =~ /^lcall\s+(\S+)\s*(.*?)\s*->\s*(.*\S)/i) { $variable = $1; $params = $2; $code = $3; process_action_parens($params, $code); if ($variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } ($evalok, $codeptr) = SEC::call_eval($code, 0); if (!$evalok || !defined($codeptr) || ref($codeptr) ne "CODE") { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Eval '$code' didn't return a code reference:", defined($codeptr)?"$codeptr":"undef"); return INVALIDVALUE; } return (LCALL, substr($variable, 1), $codeptr, [ split(' ', $params) ]); } elsif ($action =~ /^rewrite\s+(\S+)\s*(.*)/i) { $count = $1; $event = $2; process_action_parens($count, $event); if (!length($count)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Empty linecount given for rewrite action"); return INVALIDVALUE; } if (!length($event)) { $event = "%s"; } return (REWRITE, $count, $event); } elsif ($action =~ /^if\s/i) { $value = EXPRSYMBOL; if ($action2 =~ /^if\s+(\S+)\s+$value\s+else\s+$value$/i) { $variable = $1; $actionlist = $list->[0]; $actionlist2 = $list->[1]; } elsif ($action2 =~ /^if\s+(\S+)\s+$value$/i) { $variable = $1; $actionlist = $list->[0]; $actionlist2 = ""; } else { return INVALIDVALUE; } if ($variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } if ($actionlist =~ /^\s*$/) { @action = (); } elsif (!analyze_actionlist($actionlist, \@action, $conffile, $lineno, $ruleid)) { return INVALIDVALUE; } if ($actionlist2 =~ /^\s*$/) { @action2 = (); } elsif (!analyze_actionlist($actionlist2, \@action2, $conffile, $lineno, $ruleid)) { return INVALIDVALUE; } if (!scalar(@action) && !scalar(@action2)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "empty action lists given for if-else"); return INVALIDVALUE; } return (IF, substr($variable, 1), [ @action ], [ @action2 ]); } elsif ($action =~ /^while\s/i) { $value = EXPRSYMBOL; if ($action2 =~ /^while\s+(\S+)\s+$value$/i) { $variable = $1; $actionlist = $list->[0]; } else { return INVALIDVALUE; } if ($variable !~ /^%[[:alpha:]]\w*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Variable $variable does not have the form", "%[||]..."); return INVALIDVALUE; } if ($actionlist =~ /^\s*$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "empty action list given for while"); return INVALIDVALUE; } if (!analyze_actionlist($actionlist, \@action, $conffile, $lineno, $ruleid)) { return INVALIDVALUE; } return (WHILE, substr($variable, 1), [ @action ]); } elsif ($action =~ /^break$/i) { return BREAK; } elsif ($action =~ /^continue$/i) { return CONTINUE; } log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action '$action'"); return INVALIDVALUE; } # Parameters: par1 - action list separated by semicolons # par2 - reference to an array # par3 - the name of the configuration file # par4 - line number in configuration file # par5 - rule ID # Action: par1 will be split to parts, each part is analyzed and saved # to array @{$par2}. Previous content of the array is erased. # Parameters par3..par5 will be passed to the analyze_action() # function for logging purposes. Return 0 if an invalid action # was detected in the list par1, otherwise return 1. sub analyze_actionlist { my($actionlist, $arrayref, $conffile, $lineno, $ruleid) = @_; my(@parts, $part, $part2); my($actiontype, @action); my($newactionlist, @list, @list2, $expr); my($pos, $l); @{$arrayref} = (); # remove leading and trailing whitespace from actionlist if ($actionlist =~ /^\s*(.*?)\s*$/) { $actionlist = $1; } # replace the actions that are in parentheses with special symbols # and save the actions to @list $newactionlist = replace_subexpr($actionlist, \@list); if (!defined($newactionlist)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Unbalanced parentheses found in action list '$actionlist'"); return 0; } # split actionlist into parts by semicolon, removing # all whitespace before and after semicolons @parts = split(/\s*;\s*/, $newactionlist); $l = length(EXPRSYMBOL); foreach $part (@parts) { # substitute special symbols with expressions # that were removed previously $part2 = $part; @list2 = (); for (;;) { $pos = index($part, EXPRSYMBOL); if ($pos == -1) { last; } $expr = shift @list; substr($part, $pos, $l) = "(" . $expr . ")"; push @list2, $expr; } # analyze the action list part ($actiontype, @action) = analyze_action($part, $conffile, $lineno, $ruleid, $part2, \@list2); if ($actiontype == INVALIDVALUE) { return 0; } push @{$arrayref}, $actiontype, @action; } return 1; } # Parameters: par1 - context expression # par2 - reference to an array # Action: par1 will be analyzed and saved to array par2 (it is assumed # that par1 does not contain expressions in parentheses). Previous # content of the array par2 is erased. If errors are found when # analyzing par1, 0 will be returned, otherwise 1 will be returned. sub analyze_context_expr { my($context, $result) = @_; my($pos, $oper, $op1, $op2); my(@side1, @side2); my($evalok, $retval); # if we are parsing '&&' and '||' operators that take 2 operands, # process the context expression from the end with rindex(), in order # to get "from left to right" processing for AND and OR at runtime $pos = rindex($context, "||"); if ($pos != -1) { $op1 = substr($context, 0, $pos); $op2 = substr($context, $pos + 2); if (!analyze_context_expr($op1, \@side1)) { return 0; } if (!analyze_context_expr($op2, \@side2)) { return 0; } @{$result} = ( @side1, @side2, OR ); return 1; } $pos = rindex($context, "&&"); if ($pos != -1) { $op1 = substr($context, 0, $pos); $op2 = substr($context, $pos + 2); if (!analyze_context_expr($op1, \@side1)) { return 0; } if (!analyze_context_expr($op2, \@side2)) { return 0; } @{$result} = ( @side1, @side2, AND ); return 1; } # check for possible typos for '!' operator (any preceding illegal symbols) $pos = index($context, "!"); if ($pos != -1) { $op1 = substr($context, 0, $pos); $op2 = substr($context, $pos + 1); if ($op1 !~ /^\s*$/) { return 0; } if (!analyze_context_expr($op2, \@side2)) { return 0; } @{$result} = ( @side2, NEGATION ); return 1; } # since CCODE, ECODE and OPERAND are terminals, make sure that any # leading and trailing whitespace is removed from their parameters # (rest of the code relies on that); also, remove backslashes in front # of the parentheses if ($context =~ /^\s*(.*?)\s*(->|:>)\s*(.*\S)/) { $op1 = $1; $oper = $2; $op2 = $3; if ($op1 ne EXPRSYMBOL) { $op1 =~ s/\\([()])/$1/g; $op1 = [ split(' ', $op1) ]; } if ($op2 ne EXPRSYMBOL) { $op2 =~ s/\\([()])/$1/g; ($evalok, $retval) = SEC::call_eval($op2, 0); if (!$evalok || !defined($retval) || ref($retval) ne "CODE") { log_msg(LOG_ERR, "Eval '$op2' didn't return a code reference:", defined($retval)?"$retval":"undef"); return 0; } $op2 = $retval; } if ($oper eq "->") { @{$result} = ( CCODE, $op1, $op2 ); } else { @{$result} = ( CCODE2, $op1, $op2 ); } return 1; } if ($context =~ /^\s*=\s*(.*\S)/) { $op1 = $1; if ($op1 ne EXPRSYMBOL) { $op1 =~ s/\\([()])/$1/g; } @{$result} = ( ECODE, $op1 ); return 1; } if ($context =~ /^\s*varset\s+(\S+)\s*$/) { $op1 = $1; if ($op1 ne EXPRSYMBOL) { $op1 =~ s/\\([()])/$1/g; } @{$result} = ( VARSET, $op1 ); return 1; } if ($context =~ /^\s*(\S+)\s*$/) { $op1 = $1; if ($op1 ne EXPRSYMBOL) { $op1 =~ s/\\([()])/$1/g; } @{$result} = ( OPERAND, $op1 ); return 1; } return 0; } # Parameters: par1 - context expression # par2 - reference to an array # Action: par1 will be analyzed and saved to array par2. Previous content # of the array par2 is erased. If errors are found when analyzing # par1, 0 will be returned, otherwise 1 will be returned. sub analyze_context { my($context, $result) = @_; my($newcontext, $i, $j); my($params, $code, $evalok, $retval); my($subexpr, @expr); # replace upper level expressions in parentheses with special symbol # and save the expressions to @expr (i.e. !(a && (b || c )) || d # becomes !specialsymbol || d, and "a && (b || c )" is saved to @expr); # if context was not parsed successfully, exit $newcontext = replace_subexpr($context, \@expr); if (!defined($newcontext)) { return 0; } # convert context expression to internal format, and if no parenthesized # subexpressions were found in the expression during previous step, exit if (!analyze_context_expr($newcontext, $result)) { return 0; } if ($newcontext eq $context) { return 1; } # If the expression contains parenthesized subexpressions, analyze and # convert these expressions recursively, attaching the results to # the current expression. If a parenthesized subexpression is a Perl code, # it will not be analyzed recursively but rather treated as a terminal # (backslashes in front of the parentheses are removed) $i = 0; $j = scalar(@{$result}); while ($i < $j) { if ($result->[$i] == OPERAND) { if ($result->[$i+1] eq EXPRSYMBOL) { $result->[$i] = EXPRESSION; $result->[$i+1] = []; $subexpr = shift @expr; if (!analyze_context($subexpr, $result->[$i+1])) { return 0; } } $i += 2; } elsif ($result->[$i] == ECODE) { if ($result->[$i+1] eq EXPRSYMBOL) { $code = shift @expr; $code =~ s/\\([()])/$1/g; $result->[$i+1] = $code; } $i += 2; } elsif ($result->[$i] == CCODE || $result->[$i] == CCODE2) { if ($result->[$i+1] eq EXPRSYMBOL) { $params = shift @expr; $params =~ s/\\([()])/$1/g; $result->[$i+1] = [ split(' ', $params) ]; } if ($result->[$i+2] eq EXPRSYMBOL) { $code = shift @expr; $code =~ s/\\([()])/$1/g; ($evalok, $retval) = SEC::call_eval($code, 0); if (!$evalok || !defined($retval) || ref($retval) ne "CODE") { log_msg(LOG_ERR, "Eval '$code' didn't return a code reference:", defined($retval)?$retval:"undef"); return 0; } $result->[$i+2] = $retval; } $i += 3; } elsif ($result->[$i] == VARSET) { if ($result->[$i+1] eq EXPRSYMBOL) { $subexpr = shift @expr; $subexpr =~ s/\\([()])/$1/g; $result->[$i+1] = $subexpr; } $i += 2; } else { ++$i; } } return 1; } # Parameters: par1 - context expression # Action: if par1 is surrounded by [] brackets, the brackets will be # removed and 1 will be returned, otherwise 0 will be returned. sub check_context_preeval { if ($_[0] =~ /^\s*\[(.*)\]\s*$/) { $_[0] = $1; return 1; } else { return 0; } } # Parameters: par1 - list of the time values # par2 - minimum possible value for time # par3 - maximum possible value for time # par4 - offset that must be added to every list value # par5 - reference to a hash where every list value is added # Action: take the list definition and find the time values that belong # to the list (list definition is given in crontab-style). # After the values have been calculated, add an element to par5 with # the key that equals to the calculated value + offset. Leading zeros # are removed from keys (rest of the code relies on that). E.g., if # offset is 0, then "02,5-07" becomes 2,5,6,7; if offset is -1, min # is 1, and max is 12, then "2,5-7,11-" becomes 1,4,5,6,10,11. Before # adding elements to par5, its previous content is erased. If par1 is # specified incorrectly, return value is 0, otherwise 1 is returned. sub eval_timelist { my($spec, $min, $max, $offset, $ref) = @_; my(@parts, $part, $step); my($pos, $range1, $range2); my($i, $j); # split time specification into parts (by comma) and look what # ranges or individual numbers every part defines @parts = split(/,/, $spec); if (!scalar(@parts)) { return 0; } %{$ref} = (); foreach $part (@parts) { # if part is empty, skip it and take the next part if (!length($part)) { next; } # check if part has a valid step value (0 is illegal) if ($part =~ /^(.+)\/0*(\d+)$/) { $part = $1; $step = $2; if ($step == 0) { return 0; } } else { $step = undef; } # if part equals to '*', assume that it defines the range min..max if ($part eq "*") { # add offset (this also forces numeric context, so "05" becomes "5") # and save values to the hash; if step was not defined, assume 1 $i = $min + $offset; $j = $max + $offset; if (!defined($step)) { $step = 1; } while ($i <= $j) { $ref->{$i} = 1; $i += $step; } next; } # if part is not empty and not '*', check if it contains '-' $pos = index($part, "-"); if ($pos == -1) { # if part does not contain '-', assume it defines a single number if ($part =~ /^0*(\d+)$/) { $part = $1; } else { return 0; } if ($part < $min || $part > $max) { return 0; } # step value is illegal for a single number if (defined($step)) { return 0; } # add offset and save value to the hash $part += $offset; $ref->{$part} = 1; } else { # if part does contain '-', assume it defines a range $range1 = substr($part, 0, $pos); $range2 = substr($part, $pos + 1); # if left side of the range is missing, assume minimum for the value; # if right side of the range is missing, assume maximum for the value; # offset is then added to the left and right side of the range if (length($range1)) { if ($range1 =~ /^0*(\d+)$/) { $range1 = $1; } else { return 0; } if ($range1 < $min || $range1 > $max) { return 0; } $i = $range1 + $offset; } else { $i = $min + $offset; } if (length($range2)) { if ($range2 =~ /^0*(\d+)$/) { $range2 = $1; } else { return 0; } if ($range2 < $min || $range2 > $max) { return 0; } $j = $range2 + $offset; } else { $j = $max + $offset; } # save values to the hash; if step was not defined, assume 1 if (!defined($step)) { $step = 1; } while ($i <= $j) { $ref->{$i} = 1; $i += $step; } } } return 1; } # Parameters: par1 - time specification # par2..par7 - references to the hashes of minutes, hours, # days, months, weekdays and years # par8 - the name of the configuration file # par9 - line number in configuration file # Action: par1 will be split to parts, every part is analyzed and # results are saved into hashes par2..par6. # Previous content of the hashes is erased. If errors # are found when analyzing par1, 0 is returned, otherwise 1 # will be return value. sub analyze_timespec { my($timespec, $minref, $hourref, $dayref, $monthref, $wdayref, $yearref, $conffile, $lineno) = @_; my(@parts, $size); # split time specification into parts by whitespace (like with # split(/\s+/, ...)), but leading whitespace will be ignored @parts = split(' ', $timespec); $size = scalar(@parts); if ($size < 5 || $size > 6) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Wrong number of elements in time specification"); return 0; } # if no year specification has been given, assume * if ($size == 5) { push @parts, "*"; } # evaluate minute specification (range 0..59, offset 0) if (!eval_timelist($parts[0], 0, 59, 0, $minref)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid minute specification '$parts[0]'"); return 0; } # evaluate hour specification (range 0..23, offset 0) if (!eval_timelist($parts[1], 0, 23, 0, $hourref)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid hour specification '$parts[1]'"); return 0; } # evaluate day specification (range 0..31, offset 0) # 0 denotes the last day of a month if (!eval_timelist($parts[2], 0, 31, 0, $dayref)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid day specification '$parts[2]'"); return 0; } # evaluate month specification (range 1..12, offset -1) if (!eval_timelist($parts[3], 1, 12, -1, $monthref)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid month specification '$parts[3]'"); return 0; } # evaluate weekday specification (range 0..7, offset 0) if (!eval_timelist($parts[4], 0, 7, 0, $wdayref)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid weekday specification '$parts[4]'"); return 0; } # if 7 was specified as a weekday, also define 0, # since perl uses only 0 for Sunday if (exists($wdayref->{"7"})) { $wdayref->{"0"} = 1; } # evaluate year specification (range 0..99, offset 0) if (!eval_timelist($parts[5], 0, 99, 0, $yearref)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid year specification '$parts[5]'"); return 0; } return 1; } # Parameters: par1 - reference to a hash containing the rule # par2 - hash of required and optional keywords for the rule # par3 - the type of the rule # par4 - the name of the configuration file # par5 - line number in configuration file the rule begins at # Action: check if all required keywords are present in the rule par1 and # all keywords are legal (i.e., present in hash par2). # Return 0 if keywords are OK, otherwise return 1. sub missing_keywords { my($ref, $keyhash, $type, $conffile, $lineno) = @_; my($key, $error); $error = 0; # check if all required keywords are present in the rule while ($key = each %{$keyhash}) { if ($keyhash->{$key} && !exists($ref->{$key})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Keyword '$key' missing (needed for $type rule)"); $error = 1; } } # check if all rule keywords are legal while ($key = each %{$ref}) { if (!exists($keyhash->{$key})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Keyword '$key' illegal (not allowed for $type rule)"); $error = 1; } } return $error; } # Parameters: par1 - reference to a hash containing the rule # par2 - name of the configuration file # par3 - line number in configuration file the rule begins at # par4 - rule ID # Action: check the rule par1 for correctness and save it to # global array $configuration{par2} if it is well-defined; # if the rule specified rule file options, save the options to # global array $config_options{par2} if the rule is well-defined. # For a correctly defined Options-rule return 2, for a correctly # defined regular rule return 1, for an invalid rule return 0 sub check_rule { my($ref, $conffile, $lineno, $number) = @_; my($type, $rule, %keywords); my($ncomp, $cfset, $evtnum, $i, $j); if (!exists($ref->{"type"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Keyword 'type' missing"); return 0; } $type = uc($ref->{"type"}); # ------------------------------------------------------------ # SINGLE rule # ------------------------------------------------------------ if ($type eq "SINGLE") { %keywords = ("type" => 1, "continue" => 0, "ptype" => 1, "pattern" => 1, "varmap" => 0, "context" => 0, "desc" => 1, "action" => 1); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => SINGLE, "VarMap" => {}, "Context" => [], "Desc" => $ref->{"desc"}, "Action" => [], "MatchCount" => 0, "LineNo" => $lineno }; if (exists($ref->{"continue"})) { ($rule->{"WhatNext"}, $rule->{"GotoRule"}) = analyze_continue($ref->{"continue"}, $conffile, $lineno); if ($rule->{"WhatNext"} == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNext"} = DONTCONT; $rule->{"GotoRule"} = undef; } ($rule->{"PatType"}, $rule->{"PatLines"}, $rule->{"Pattern"}) = analyze_pattern($ref->{"ptype"}, $ref->{"pattern"}, $conffile, $lineno); if ($rule->{"PatType"} == INVALIDVALUE) { return 0; } if (exists($ref->{"varmap"})) { if (!analyze_varmap($rule->{"PatType"}, $ref->{"varmap"}, $rule->{"VarMap"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"context"})) { if (check_context_preeval($ref->{"context"})) { $rule->{"ContPreEval"} = 1; } if (!analyze_context($ref->{"context"}, $rule->{"Context"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context"}, "'"); return 0; } if (volatile_context($rule->{"Context"}, '$')) { $rule->{"ContVolat"} = 1; } } if (!analyze_actionlist($ref->{"action"}, $rule->{"Action"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action"}, "'"); return 0; } if (contains_matchvars($ref->{"action"}, '$')) { $rule->{"ActVolat"} = 1; } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # SINGLE_W_SCRIPT rule # ------------------------------------------------------------ elsif ($type eq "SINGLEWITHSCRIPT") { %keywords = ("type" => 1, "continue" => 0, "ptype" => 1, "pattern" => 1, "varmap" => 0, "context" => 0, "script" => 1, "desc" => 1, "action" => 1, "action2" => 0); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => SINGLE_W_SCRIPT, "VarMap" => {}, "Context" => [], "Script" => $ref->{"script"}, "Desc" => $ref->{"desc"}, "Action" => [], "Action2" => [], "MatchCount" => 0, "LineNo" => $lineno }; if (exists($ref->{"continue"})) { ($rule->{"WhatNext"}, $rule->{"GotoRule"}) = analyze_continue($ref->{"continue"}, $conffile, $lineno); if ($rule->{"WhatNext"} == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNext"} = DONTCONT; $rule->{"GotoRule"} = undef; } ($rule->{"PatType"}, $rule->{"PatLines"}, $rule->{"Pattern"}) = analyze_pattern($ref->{"ptype"}, $ref->{"pattern"}, $conffile, $lineno); if ($rule->{"PatType"} == INVALIDVALUE) { return 0; } if (exists($ref->{"varmap"})) { if (!analyze_varmap($rule->{"PatType"}, $ref->{"varmap"}, $rule->{"VarMap"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"context"})) { if (check_context_preeval($ref->{"context"})) { $rule->{"ContPreEval"} = 1; } if (!analyze_context($ref->{"context"}, $rule->{"Context"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context"}, "'"); return 0; } if (volatile_context($rule->{"Context"}, '$')) { $rule->{"ContVolat"} = 1; } } if (!analyze_actionlist($ref->{"action"}, $rule->{"Action"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action"}, "'"); return 0; } if (contains_matchvars($ref->{"action"}, '$')) { $rule->{"ActVolat"} = 1; } if (exists($ref->{"action2"})) { if (!analyze_actionlist($ref->{"action2"}, $rule->{"Action2"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action2"}, "'"); return 0; } if (contains_matchvars($ref->{"action2"}, '$')) { $rule->{"ActVolat2"} = 1; } } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # SINGLE_W_SUPPRESS rule # ------------------------------------------------------------ elsif ($type eq "SINGLEWITHSUPPRESS") { %keywords = ("type" => 1, "continue" => 0, "ptype" => 1, "pattern" => 1, "varmap" => 0, "context" => 0, "desc" => 1, "action" => 1, "window" => 1); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => SINGLE_W_SUPPRESS, "VarMap" => {}, "Context" => [], "Desc" => $ref->{"desc"}, "Action" => [], "MatchCount" => 0, "LineNo" => $lineno }; if (exists($ref->{"continue"})) { ($rule->{"WhatNext"}, $rule->{"GotoRule"}) = analyze_continue($ref->{"continue"}, $conffile, $lineno); if ($rule->{"WhatNext"} == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNext"} = DONTCONT; $rule->{"GotoRule"} = undef; } ($rule->{"PatType"}, $rule->{"PatLines"}, $rule->{"Pattern"}) = analyze_pattern($ref->{"ptype"}, $ref->{"pattern"}, $conffile, $lineno); if ($rule->{"PatType"} == INVALIDVALUE) { return 0; } if (exists($ref->{"varmap"})) { if (!analyze_varmap($rule->{"PatType"}, $ref->{"varmap"}, $rule->{"VarMap"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"context"})) { if (check_context_preeval($ref->{"context"})) { $rule->{"ContPreEval"} = 1; } if (!analyze_context($ref->{"context"}, $rule->{"Context"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context"}, "'"); return 0; } if (volatile_context($rule->{"Context"}, '$')) { $rule->{"ContVolat"} = 1; } } if (!analyze_actionlist($ref->{"action"}, $rule->{"Action"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action"}, "'"); return 0; } if (contains_matchvars($ref->{"action"}, '$')) { $rule->{"ActVolat"} = 1; } if ($ref->{"window"} !~ /^0*(\d+)$/ || $1 == 0) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid time window '", $ref->{"window"}, "'"); return 0; } else { $rule->{"Window"} = $1; } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # PAIR rule # ------------------------------------------------------------ elsif ($type eq "PAIR") { %keywords = ("type" => 1, "continue" => 0, "ptype" => 1, "pattern" => 1, "varmap" => 0, "context" => 0, "desc" => 1, "action" => 1, "continue2" => 0, "ptype2" => 1, "pattern2" => 1, "varmap2" => 0, "context2" => 0, "desc2" => 1, "action2" => 1, "window" => 0); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => PAIR, "VarMap" => {}, "VarMap2" => {}, "Context" => [], "Context2" => [], "Desc" => $ref->{"desc"}, "Desc2" => $ref->{"desc2"}, "Action" => [], "Action2" => [], "Operations" => {}, "MatchCount" => 0, "LineNo" => $lineno }; if (exists($ref->{"continue"})) { ($rule->{"WhatNext"}, $rule->{"GotoRule"}) = analyze_continue($ref->{"continue"}, $conffile, $lineno); if ($rule->{"WhatNext"} == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNext"} = DONTCONT; $rule->{"GotoRule"} = undef; } if (exists($ref->{"continue2"})) { ($rule->{"WhatNext2"}, $rule->{"GotoRule2"}) = analyze_continue($ref->{"continue2"}, $conffile, $lineno); if ($rule->{"WhatNext2"} == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNext2"} = DONTCONT; $rule->{"GotoRule2"} = undef; } ($rule->{"PatType"}, $rule->{"PatLines"}, $rule->{"Pattern"}) = analyze_pattern($ref->{"ptype"}, $ref->{"pattern"}, $conffile, $lineno); if ($rule->{"PatType"} == INVALIDVALUE) { return 0; } ($rule->{"PatType2"}, $rule->{"PatLines2"}, $rule->{"Pattern2"}, $ncomp) = analyze_pattern($ref->{"ptype2"}, $ref->{"pattern2"}, $conffile, $lineno, $rule->{"PatType"}); if ($rule->{"PatType2"} == INVALIDVALUE) { return 0; } if (defined($ncomp)) { $rule->{"Pat2NotCompiled"} = 1; } if (exists($ref->{"varmap"})) { if (!analyze_varmap($rule->{"PatType"}, $ref->{"varmap"}, $rule->{"VarMap"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"varmap2"})) { if (!analyze_varmap($rule->{"PatType2"}, $ref->{"varmap2"}, $rule->{"VarMap2"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"context"})) { if (check_context_preeval($ref->{"context"})) { $rule->{"ContPreEval"} = 1; } if (!analyze_context($ref->{"context"}, $rule->{"Context"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context"}, "'"); return 0; } if (volatile_context($rule->{"Context"}, '$')) { $rule->{"ContVolat"} = 1; } } if (exists($ref->{"context2"})) { if (check_context_preeval($ref->{"context2"})) { $rule->{"ContPreEval2"} = 1; } if (!analyze_context($ref->{"context2"}, $rule->{"Context2"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context2"}, "'"); return 0; } if (volatile_context($rule->{"Context2"}, '$', '%')) { $rule->{"ContVolat2"} = 1; } } if (!analyze_actionlist($ref->{"action"}, $rule->{"Action"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action"}, "'"); return 0; } if (contains_matchvars($ref->{"action"}, '$')) { $rule->{"ActVolat"} = 1; } if (!analyze_actionlist($ref->{"action2"}, $rule->{"Action2"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action2"}, "'"); return 0; } if (contains_matchvars($ref->{"action2"}, '$', '%')) { $rule->{"ActVolat2"} = 1; } if (!exists($ref->{"window"})) { $rule->{"Window"} = 0; } elsif ($ref->{"window"} =~ /^0*(\d+)$/) { $rule->{"Window"} = $1; } else { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid time window '", $ref->{"window"}, "'"); return 0; } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # PAIR_W_WINDOW rule # ------------------------------------------------------------ elsif ($type eq "PAIRWITHWINDOW") { %keywords = ("type" => 1, "continue" => 0, "ptype" => 1, "pattern" => 1, "varmap" => 0, "context" => 0, "desc" => 1, "action" => 1, "continue2" => 0, "ptype2" => 1, "pattern2" => 1, "varmap2" => 0, "context2" => 0, "desc2" => 1, "action2" => 1, "window" => 1); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => PAIR_W_WINDOW, "VarMap" => {}, "VarMap2" => {}, "Context" => [], "Context2" => [], "Desc" => $ref->{"desc"}, "Desc2" => $ref->{"desc2"}, "Action" => [], "Action2" => [], "Operations" => {}, "MatchCount" => 0, "LineNo" => $lineno }; if (exists($ref->{"continue"})) { ($rule->{"WhatNext"}, $rule->{"GotoRule"}) = analyze_continue($ref->{"continue"}, $conffile, $lineno); if ($rule->{"WhatNext"} == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNext"} = DONTCONT; $rule->{"GotoRule"} = undef; } if (exists($ref->{"continue2"})) { ($rule->{"WhatNext2"}, $rule->{"GotoRule2"}) = analyze_continue($ref->{"continue2"}, $conffile, $lineno); if ($rule->{"WhatNext2"} == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNext2"} = DONTCONT; $rule->{"GotoRule2"} = undef; } ($rule->{"PatType"}, $rule->{"PatLines"}, $rule->{"Pattern"}) = analyze_pattern($ref->{"ptype"}, $ref->{"pattern"}, $conffile, $lineno); if ($rule->{"PatType"} == INVALIDVALUE) { return 0; } ($rule->{"PatType2"}, $rule->{"PatLines2"}, $rule->{"Pattern2"}, $ncomp) = analyze_pattern($ref->{"ptype2"}, $ref->{"pattern2"}, $conffile, $lineno, $rule->{"PatType"}); if ($rule->{"PatType2"} == INVALIDVALUE) { return 0; } if (defined($ncomp)) { $rule->{"Pat2NotCompiled"} = 1; } if (exists($ref->{"varmap"})) { if (!analyze_varmap($rule->{"PatType"}, $ref->{"varmap"}, $rule->{"VarMap"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"varmap2"})) { if (!analyze_varmap($rule->{"PatType2"}, $ref->{"varmap2"}, $rule->{"VarMap2"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"context"})) { if (check_context_preeval($ref->{"context"})) { $rule->{"ContPreEval"} = 1; } if (!analyze_context($ref->{"context"}, $rule->{"Context"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context"}, "'"); return 0; } if (volatile_context($rule->{"Context"}, '$')) { $rule->{"ContVolat"} = 1; } } if (exists($ref->{"context2"})) { if (check_context_preeval($ref->{"context2"})) { $rule->{"ContPreEval2"} = 1; } if (!analyze_context($ref->{"context2"}, $rule->{"Context2"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context2"}, "'"); return 0; } if (volatile_context($rule->{"Context2"}, '$', '%')) { $rule->{"ContVolat2"} = 1; } } if (!analyze_actionlist($ref->{"action"}, $rule->{"Action"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action"}, "'"); return 0; } if (contains_matchvars($ref->{"action"}, '$')) { $rule->{"ActVolat"} = 1; } if (!analyze_actionlist($ref->{"action2"}, $rule->{"Action2"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action2"}, "'"); return 0; } if (contains_matchvars($ref->{"action2"}, '$', '%')) { $rule->{"ActVolat2"} = 1; } if ($ref->{"window"} !~ /^0*(\d+)$/ || $1 == 0) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid time window '", $ref->{"window"}, "'"); return 0; } else { $rule->{"Window"} = $1; } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # SINGLE_W_THRESHOLD rule # ------------------------------------------------------------ elsif ($type eq "SINGLEWITHTHRESHOLD") { %keywords = ("type" => 1, "continue" => 0, "ptype" => 1, "pattern" => 1, "varmap" => 0, "context" => 0, "desc" => 1, "action" => 1, "action2" => 0, "window" => 1, "thresh" => 1); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => SINGLE_W_THRESHOLD, "VarMap" => {}, "Context" => [], "Desc" => $ref->{"desc"}, "Action" => [], "Action2" => [], "MatchCount" => 0, "LineNo" => $lineno }; if (exists($ref->{"continue"})) { ($rule->{"WhatNext"}, $rule->{"GotoRule"}) = analyze_continue($ref->{"continue"}, $conffile, $lineno); if ($rule->{"WhatNext"} == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNext"} = DONTCONT; $rule->{"GotoRule"} = undef; } ($rule->{"PatType"}, $rule->{"PatLines"}, $rule->{"Pattern"}) = analyze_pattern($ref->{"ptype"}, $ref->{"pattern"}, $conffile, $lineno); if ($rule->{"PatType"} == INVALIDVALUE) { return 0; } if (exists($ref->{"varmap"})) { if (!analyze_varmap($rule->{"PatType"}, $ref->{"varmap"}, $rule->{"VarMap"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"context"})) { if (check_context_preeval($ref->{"context"})) { $rule->{"ContPreEval"} = 1; } if (!analyze_context($ref->{"context"}, $rule->{"Context"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context"}, "'"); return 0; } if (volatile_context($rule->{"Context"}, '$')) { $rule->{"ContVolat"} = 1; } } if (!analyze_actionlist($ref->{"action"}, $rule->{"Action"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action"}, "'"); return 0; } if (contains_matchvars($ref->{"action"}, '$')) { $rule->{"ActVolat"} = 1; } if (exists($ref->{"action2"})) { if (!analyze_actionlist($ref->{"action2"}, $rule->{"Action2"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action2"}, "'"); return 0; } if (contains_matchvars($ref->{"action2"}, '$')) { $rule->{"ActVolat2"} = 1; } } if ($ref->{"window"} !~ /^0*(\d+)$/ || $1 == 0) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid time window '", $ref->{"window"}, "'"); return 0; } else { $rule->{"Window"} = $1; } if ($ref->{"thresh"} !~ /^0*(\d+)$/ || $1 == 0) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid threshold '", $ref->{"thresh"}, "'"); return 0; } else { $rule->{"Threshold"} = $1; } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # SINGLE_W_2_THRESHOLDS rule # ------------------------------------------------------------ elsif ($type eq "SINGLEWITH2THRESHOLDS") { %keywords = ("type" => 1, "continue" => 0, "ptype" => 1, "pattern" => 1, "varmap" => 0, "context" => 0, "desc" => 1, "action" => 1, "window" => 1, "thresh" => 1, "desc2" => 1, "action2" => 1, "window2" => 1, "thresh2" => 1); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => SINGLE_W_2_THRESHOLDS, "VarMap" => {}, "Context" => [], "Desc" => $ref->{"desc"}, "Desc2" => $ref->{"desc2"}, "Action" => [], "Action2" => [], "MatchCount" => 0, "LineNo" => $lineno }; if (exists($ref->{"continue"})) { ($rule->{"WhatNext"}, $rule->{"GotoRule"}) = analyze_continue($ref->{"continue"}, $conffile, $lineno); if ($rule->{"WhatNext"} == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNext"} = DONTCONT; $rule->{"GotoRule"} = undef; } ($rule->{"PatType"}, $rule->{"PatLines"}, $rule->{"Pattern"}) = analyze_pattern($ref->{"ptype"}, $ref->{"pattern"}, $conffile, $lineno); if ($rule->{"PatType"} == INVALIDVALUE) { return 0; } if (exists($ref->{"varmap"})) { if (!analyze_varmap($rule->{"PatType"}, $ref->{"varmap"}, $rule->{"VarMap"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"context"})) { if (check_context_preeval($ref->{"context"})) { $rule->{"ContPreEval"} = 1; } if (!analyze_context($ref->{"context"}, $rule->{"Context"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context"}, "'"); return 0; } if (volatile_context($rule->{"Context"}, '$')) { $rule->{"ContVolat"} = 1; } } if (!analyze_actionlist($ref->{"action"}, $rule->{"Action"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action"}, "'"); return 0; } if (contains_matchvars($ref->{"action"}, '$')) { $rule->{"ActVolat"} = 1; } if (!analyze_actionlist($ref->{"action2"}, $rule->{"Action2"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action2"}, "'"); return 0; } if (contains_matchvars($ref->{"action2"}, '$')) { $rule->{"ActVolat2"} = 1; } if ($ref->{"window"} !~ /^0*(\d+)$/ || $1 == 0) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid 1st time window '", $ref->{"window"}, "'"); return 0; } else { $rule->{"Window"} = $1; } if ($ref->{"window2"} !~ /^0*(\d+)$/ || $1 == 0) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid 2nd time window '", $ref->{"window2"}, "'"); return 0; } else { $rule->{"Window2"} = $1; } if ($ref->{"thresh"} !~ /^0*(\d+)$/ || $1 == 0) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid 1st threshold '", $ref->{"thresh"}, "'"); return 0; } else { $rule->{"Threshold"} = $1; } if ($ref->{"thresh2"} !~ /^0*(\d+)$/) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid 2nd threshold '", $ref->{"thresh2"}, "'"); return 0; } else { $rule->{"Threshold2"} = $1; } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # EVENT_GROUP rule # ------------------------------------------------------------ elsif ($type =~ /^EVENTGROUP(?:0*\B)?(\d*)$/) { $evtnum = length($1)?$1:1; if ($evtnum < 1) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid rule type $type (N must be at least 1 for EventGroupN rule)"); return 0; } %keywords = ("type" => 1, "desc" => 1, "action" => 1, "multact" => 0, "init" => 0, "slide" => 0, "end" => 0, "window" => 1); for ($i = 0; $i < $evtnum; ++$i) { $j = ($i==0)?"":($i+1); $keywords{"continue$j"} = 0; $keywords{"ptype$j"} = 1; $keywords{"pattern$j"} = 1; $keywords{"varmap$j"} = 0; $keywords{"context$j"} = 0; $keywords{"count$j"} = 0; $keywords{"thresh$j"} = 0; } if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => EVENT_GROUP, "EventNumber" => $evtnum, "WhatNextList" => [], "GotoRuleList" => [], "PatTypeList" => [], "PatternList" => [], "PatLinesList" => [], "VarMapList" => [], "ContextList" => [], "ContPreEvalList" => {}, "ContVolatList" => {}, "CountActionList" => [], "CountActVolatList" => {}, "ThresholdList" => [], "Desc" => $ref->{"desc"}, "Action" => [], "InitAction" => [], "SlideAction" => [], "EndAction" => [], "MatchCount" => 0, "LineNo" => $lineno }; for ($i = 0; $i < $evtnum; ++$i) { $rule->{"VarMapList"}->[$i] = {}; $rule->{"ContextList"}->[$i] = []; $rule->{"CountActionList"}->[$i] = []; $j = ($i==0)?"":($i+1); if (exists($ref->{"continue$j"})) { ($rule->{"WhatNextList"}->[$i], $rule->{"GotoRuleList"}->[$i]) = analyze_continue($ref->{"continue$j"}, $conffile, $lineno); if ($rule->{"WhatNextList"}->[$i] == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNextList"}->[$i] = DONTCONT; $rule->{"GotoRuleList"}->[$i] = undef; } ($rule->{"PatTypeList"}->[$i], $rule->{"PatLinesList"}->[$i], $rule->{"PatternList"}->[$i]) = analyze_pattern($ref->{"ptype$j"}, $ref->{"pattern$j"}, $conffile, $lineno); if ($rule->{"PatTypeList"}->[$i] == INVALIDVALUE) { return 0; } if (exists($ref->{"varmap$j"})) { if (!analyze_varmap($rule->{"PatTypeList"}->[$i], $ref->{"varmap$j"}, $rule->{"VarMapList"}->[$i], $conffile, $lineno)) { return 0; } } if (exists($ref->{"context$j"})) { if (check_context_preeval($ref->{"context$j"})) { $rule->{"ContPreEvalList"}->{$i} = 1; } if (!analyze_context($ref->{"context$j"}, $rule->{"ContextList"}->[$i])) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context$j"}, "'"); return 0; } if (volatile_context($rule->{"ContextList"}->[$i], '$')) { $rule->{"ContVolatList"}->{$i} = 1; } } if (exists($ref->{"count$j"})) { if (!analyze_actionlist($ref->{"count$j"}, $rule->{"CountActionList"}->[$i], $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"count$j"}, "'"); return 0; } if (contains_matchvars($ref->{"count$j"}, '$')) { $rule->{"CountActVolatList"}->{$i} = 1; } } if (exists($ref->{"thresh$j"})) { if ($ref->{"thresh$j"} !~ /^0*(\d+)$/ || $1 == 0) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid threshold '", $ref->{"thresh$j"}, "'"); return 0; } else { $rule->{"ThresholdList"}->[$i] = $1; } } else { $rule->{"ThresholdList"}->[$i] = 1; } } if (!analyze_actionlist($ref->{"action"}, $rule->{"Action"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action"}, "'"); return 0; } if (contains_matchvars($ref->{"action"}, '$')) { $rule->{"ActVolat"} = 1; } if (exists($ref->{"init"})) { if (!analyze_actionlist($ref->{"init"}, $rule->{"InitAction"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"init"}, "'"); return 0; } if (contains_matchvars($ref->{"init"}, '$')) { $rule->{"InitActVolat"} = 1; } } if (exists($ref->{"slide"})) { if (!analyze_actionlist($ref->{"slide"}, $rule->{"SlideAction"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"slide"}, "'"); return 0; } if (contains_matchvars($ref->{"slide"}, '$')) { $rule->{"SlideActVolat"} = 1; } } if (exists($ref->{"end"})) { if (!analyze_actionlist($ref->{"end"}, $rule->{"EndAction"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"end"}, "'"); return 0; } if (contains_matchvars($ref->{"end"}, '$')) { $rule->{"EndActVolat"} = 1; } } if ($ref->{"window"} !~ /^0*(\d+)$/ || $1 == 0) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid time window '", $ref->{"window"}, "'"); return 0; } else { $rule->{"Window"} = $1; } if (exists($ref->{"multact"})) { if (uc($ref->{"multact"}) eq "YES") { $rule->{"MultipleActions"} = 1; } elsif (uc($ref->{"multact"}) ne "NO") { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid multact value '", $ref->{"multact"}, "'"); return 0; } } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # SUPPRESS rule # ------------------------------------------------------------ elsif ($type eq "SUPPRESS") { %keywords = ("type" => 1, "ptype" => 1, "pattern" => 1, "varmap" => 0, "context" => 0, "desc" => 0); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => SUPPRESS, "VarMap" => {}, "Context" => [], "MatchCount" => 0, "LineNo" => $lineno }; ($rule->{"PatType"}, $rule->{"PatLines"}, $rule->{"Pattern"}) = analyze_pattern($ref->{"ptype"}, $ref->{"pattern"}, $conffile, $lineno); if ($rule->{"PatType"} == INVALIDVALUE) { return 0; } if (exists($ref->{"varmap"})) { if (!analyze_varmap($rule->{"PatType"}, $ref->{"varmap"}, $rule->{"VarMap"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"context"})) { if (check_context_preeval($ref->{"context"})) { $rule->{"ContPreEval"} = 1; } if (!analyze_context($ref->{"context"}, $rule->{"Context"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context"}, "'"); return 0; } if (volatile_context($rule->{"Context"}, '$')) { $rule->{"ContVolat"} = 1; } } if (!exists($ref->{"desc"})) { if ($rule->{"PatType"} == REGEXP || $rule->{"PatType"} == SUBSTR || $rule->{"PatType"} == PERLFUNC || $rule->{"PatType"} == CACHED) { $rule->{"Desc"} = "Suppress rule with pattern: " . $rule->{"Pattern"}; } elsif ($rule->{"PatType"} == NREGEXP || $rule->{"PatType"} == NSUBSTR || $rule->{"PatType"} == NPERLFUNC || $rule->{"PatType"} == NCACHED) { $rule->{"Desc"} = "Suppress rule with negative pattern: " . $rule->{"Pattern"}; } else { $rule->{"Desc"} = "Suppress rule with pattern: " . ($rule->{"Pattern"}?"TRUE":"FALSE"); } } else { $rule->{"Desc"} = $ref->{"desc"}; } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # CALENDAR rule # ------------------------------------------------------------ elsif ($type eq "CALENDAR") { %keywords = ("type" => 1, "time" => 1, "context" => 0, "desc" => 1, "action" => 1); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => CALENDAR, "Minutes" => {}, "Hours" => {}, "Days" => {}, "Months" => {}, "Weekdays" => {}, "Years" => {}, "LastActionMinute" => 0, "LastActionHour" => 0, "LastActionDay" => 0, "LastActionMonth" => 0, "LastActionYear" => 0, "Context" => [], "Desc" => $ref->{"desc"}, "Action" => [], "MatchCount" => 0, "LineNo" => $lineno }; if (!analyze_timespec($ref->{"time"}, $rule->{"Minutes"}, $rule->{"Hours"}, $rule->{"Days"}, $rule->{"Months"}, $rule->{"Weekdays"}, $rule->{"Years"}, $conffile, $lineno)) { return 0; } # since for Calendar rule []-operator has no meaning, remove outer # square brackets if they exist, and don't set the ContPreEval flag if (exists($ref->{"context"})) { check_context_preeval($ref->{"context"}); if (!analyze_context($ref->{"context"}, $rule->{"Context"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context"}, "'"); return 0; } } if (!analyze_actionlist($ref->{"action"}, $rule->{"Action"}, $conffile, $lineno, $number)) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid action list '", $ref->{"action"}, "'"); return 0; } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # JUMP rule # ------------------------------------------------------------ elsif ($type eq "JUMP") { %keywords = ("type" => 1, "continue" => 0, "ptype" => 1, "pattern" => 1, "varmap" => 0, "context" => 0, "cfset" => 0, "constset" => 0, "desc" => 0); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } $rule = { "ID" => $number, "Type" => JUMP, "VarMap" => {}, "Context" => [], "MatchCount" => 0, "LineNo" => $lineno }; if (exists($ref->{"continue"})) { ($rule->{"WhatNext"}, $rule->{"GotoRule"}) = analyze_continue($ref->{"continue"}, $conffile, $lineno); if ($rule->{"WhatNext"} == INVALIDVALUE) { return 0; } } else { $rule->{"WhatNext"} = DONTCONT; $rule->{"GotoRule"} = undef; } ($rule->{"PatType"}, $rule->{"PatLines"}, $rule->{"Pattern"}) = analyze_pattern($ref->{"ptype"}, $ref->{"pattern"}, $conffile, $lineno); if ($rule->{"PatType"} == INVALIDVALUE) { return 0; } if (exists($ref->{"varmap"})) { if (!analyze_varmap($rule->{"PatType"}, $ref->{"varmap"}, $rule->{"VarMap"}, $conffile, $lineno)) { return 0; } } if (exists($ref->{"context"})) { if (check_context_preeval($ref->{"context"})) { $rule->{"ContPreEval"} = 1; } if (!analyze_context($ref->{"context"}, $rule->{"Context"})) { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid context specification '", $ref->{"context"}, "'"); return 0; } if (volatile_context($rule->{"Context"}, '$')) { $rule->{"ContVolat"} = 1; } } if (!exists($ref->{"desc"})) { if ($rule->{"PatType"} == REGEXP || $rule->{"PatType"} == SUBSTR || $rule->{"PatType"} == PERLFUNC || $rule->{"PatType"} == CACHED) { $rule->{"Desc"} = "Jump rule with pattern: " . $rule->{"Pattern"}; } elsif ($rule->{"PatType"} == NREGEXP || $rule->{"PatType"} == NSUBSTR || $rule->{"PatType"} == NPERLFUNC || $rule->{"PatType"} == NCACHED) { $rule->{"Desc"} = "Jump rule with negative pattern: " . $rule->{"Pattern"}; } else { $rule->{"Desc"} = "Jump rule with pattern: " . ($rule->{"Pattern"}?"TRUE":"FALSE"); } } else { $rule->{"Desc"} = $ref->{"desc"}; } if (exists($ref->{"cfset"})) { $rule->{"CFSet"} = [ split(' ', $ref->{"cfset"}) ]; } if (exists($ref->{"constset"})) { if (uc($ref->{"constset"}) eq "YES") { $rule->{"ConstSet"} = 1; } elsif (uc($ref->{"constset"}) ne "NO") { log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid constset value '", $ref->{"constset"}, "'"); return 0; } } else { $rule->{"ConstSet"} = 1; } $configuration{$conffile}->[$number] = $rule; return 1; } # ------------------------------------------------------------ # OPTIONS rule # ------------------------------------------------------------ elsif ($type eq "OPTIONS") { %keywords = ("type" => 1, "joincfset" => 0, "procallin" => 0); if (missing_keywords($ref, \%keywords, $type, $conffile, $lineno)) { return 0; } # discard any previous Options rule $config_options{$conffile} = {}; # parse and save the procallin value; assume default for invalid value if (exists($ref->{"procallin"})) { if (uc($ref->{"procallin"}) eq "NO") { $config_options{$conffile}->{"JumpOnly"} = 1; } elsif (uc($ref->{"procallin"}) ne "YES") { log_msg(LOG_WARN, "Rule in $conffile at line $lineno:", "Invalid procallin value '", $ref->{"procallin"}, "', assuming procallin=Yes"); } } # parse and save the list of set names if (exists($ref->{"joincfset"})) { $config_options{$conffile}->{"CFSet"} = {}; foreach $cfset (split(' ', $ref->{"joincfset"})) { $config_options{$conffile}->{"CFSet"}->{$cfset} = 1; } } return 2; } # ------------------------------------------------------------ # end of rule processing # ------------------------------------------------------------ log_msg(LOG_ERR, "Rule in $conffile at line $lineno:", "Invalid rule type $type"); return 0; } # Parameters: par1 - name of the configuration file # par2 - reference to the hash of label->rule conversion # Action: process continue-statements of rules of configuration file par1, # and resolve labels in 'continue=GoTo